46ff6b7528eec3628c3a23ebb6d4452b1bff7b8d
[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_purged (part) && ((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 remove_line_breaks (gchar *str)
1156 {
1157         gchar *needle = g_strrstr (str, "\r\n");
1158         if (needle)
1159                 *needle = '\0';
1160 }
1161
1162 static void 
1163 set_references (TnyMsg *reply_msg, TnyMsg *original_msg)
1164 {
1165         gchar *orig_references, *orig_in_reply_to, *orig_message_id;
1166         gchar *references, *in_reply_to;
1167
1168         modest_tny_msg_get_references (original_msg, &orig_message_id, &orig_references, &orig_in_reply_to);
1169
1170         references = NULL;
1171         in_reply_to = NULL;
1172
1173         if (orig_message_id)
1174                 in_reply_to = g_strdup (orig_message_id);
1175
1176         if (orig_references) {
1177                 if (orig_message_id)
1178                         references = g_strconcat (orig_references, "\n        ", orig_message_id, NULL);
1179                 else
1180                         references = g_strdup (orig_references);
1181
1182         } else if (orig_in_reply_to) {
1183                 if (orig_message_id)
1184                         references = g_strconcat (orig_in_reply_to, "\n        ", orig_message_id, NULL);
1185                 else
1186                         references = g_strdup (orig_in_reply_to);
1187         } else if (orig_message_id) {
1188                 references = g_strdup (orig_message_id);
1189         }
1190
1191         g_free (orig_references);
1192         g_free (orig_in_reply_to);
1193         g_free (orig_message_id);
1194
1195         if (in_reply_to) {
1196                 remove_line_breaks (in_reply_to);
1197                 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "In-Reply-To", in_reply_to);
1198                 g_free (in_reply_to);
1199         }
1200         if (references) {
1201                 remove_line_breaks (references);
1202                 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "References", references);
1203                 g_free (references);
1204         }
1205 }
1206
1207 TnyMsg*
1208 modest_tny_msg_create_reply_msg (TnyMsg *msg,
1209                                  TnyHeader *header,
1210                                  const gchar *from,
1211                                  const gchar *signature,
1212                                  ModestTnyMsgReplyType reply_type,
1213                                  ModestTnyMsgReplyMode reply_mode)
1214 {
1215         TnyMsg *new_msg = NULL;
1216         TnyHeader *new_header;
1217         gchar *new_to = NULL;
1218         TnyList *parts = NULL;
1219         GList *attachments_list = NULL;
1220         TnyMimePart *part_to_check = NULL;
1221
1222         g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1223
1224         part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
1225
1226         parts = TNY_LIST (tny_simple_list_new());
1227         tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
1228         tny_list_foreach (parts, add_if_attachment, &attachments_list);
1229
1230         new_msg = create_reply_forward_mail (msg, header, from, signature, TRUE, reply_type,
1231                                              attachments_list);
1232
1233         set_references (new_msg, msg);
1234         if (attachments_list != NULL) {
1235                 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
1236                 g_list_free (attachments_list);
1237         }
1238         g_object_unref (parts);
1239
1240         /* Fill the header */
1241         if (header)
1242                 g_object_ref (header);
1243         else
1244                 header = tny_msg_get_header (msg);
1245
1246         
1247         new_header = tny_msg_get_header(new_msg);
1248         new_to = get_new_to (msg, header, from, reply_mode);
1249         if (!new_to)
1250                 g_debug ("%s: failed to get new To:", __FUNCTION__);
1251         else {
1252                 tny_header_set_to (new_header, new_to);
1253         }
1254
1255         if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1256                 gchar *new_cc = get_new_cc (header, from, new_to);
1257                 if (new_cc) { 
1258                         tny_header_set_cc (new_header, new_cc);
1259                         g_free (new_cc);
1260                 }
1261         }
1262
1263         if (new_to)
1264                 g_free (new_to);
1265
1266         /* Clean */
1267         g_object_unref (G_OBJECT (new_header));
1268         g_object_unref (G_OBJECT (header));
1269         g_object_unref (G_OBJECT (part_to_check));
1270
1271         return new_msg;
1272 }
1273
1274
1275 static gboolean
1276 is_ascii(const gchar *s)
1277 {
1278         if (!s)
1279                 return TRUE;
1280         while (s[0]) {
1281                 if (s[0] & 128 || s[0] < 32)
1282                         return FALSE;
1283                 s++;
1284         }
1285         return TRUE;
1286 }
1287
1288 static char *
1289 get_content_type(const gchar *s)
1290 {
1291         GString *type;
1292         
1293         type = g_string_new("text/plain");
1294         if (!is_ascii(s)) {
1295                 if (g_utf8_validate(s, -1, NULL)) {
1296                         g_string_append(type, "; charset=\"utf-8\"");
1297                 } else {
1298                         /* it should be impossible to reach this, but better safe than sorry */
1299                         g_debug("invalid utf8 in message");
1300                         g_string_append(type, "; charset=\"latin1\"");
1301                 }
1302         }
1303         return g_string_free(type, FALSE);
1304 }
1305
1306 guint64
1307 modest_tny_msg_estimate_size (const gchar *plain_body, const gchar *html_body,
1308                               guint64 parts_count,
1309                               guint64 parts_size)
1310 {
1311         guint64 result;
1312
1313         /* estimation of headers size */
1314         result = 1024;
1315
1316         /* We add a 20% of size due to the increase in 7bit encoding */
1317         if (plain_body) {
1318                 result += strlen (plain_body) * 120 / 100;
1319         }
1320         if (html_body) {
1321                 result += strlen (html_body) * 120 / 100;
1322         }
1323
1324         /* 256 bytes per additional part because of their headers */
1325         result += parts_count * 256;
1326
1327         /* 150% of increase per encoding */
1328         result += parts_size * 3 / 2;
1329
1330         return result;
1331 }
1332
1333 GSList *
1334 modest_tny_msg_header_get_all_recipients_list (TnyHeader *header)
1335 {
1336         GSList *recipients = NULL;
1337         gchar *from = NULL, *to = NULL, *cc = NULL, *bcc = NULL;
1338         gchar *after_remove, *joined;
1339
1340         if (header == NULL)
1341                 return NULL;
1342
1343         from = tny_header_dup_from (header);
1344         to = tny_header_dup_to (header);
1345         cc = tny_header_dup_cc (header);
1346         bcc = tny_header_dup_bcc (header);
1347
1348         joined = modest_text_utils_join_addresses (from, to, cc, bcc);
1349         after_remove = modest_text_utils_remove_duplicate_addresses (joined);
1350         g_free (joined);
1351
1352         recipients = modest_text_utils_split_addresses_list (after_remove);
1353         g_free (after_remove);
1354
1355         if (from)
1356                 g_free (from);
1357         if (to)
1358                 g_free (to);
1359         if (cc)
1360                 g_free (cc);
1361         if (bcc)
1362                 g_free (bcc);
1363
1364         return recipients;
1365 }
1366
1367 GSList *
1368 modest_tny_msg_get_all_recipients_list (TnyMsg *msg)
1369 {
1370         TnyHeader *header = NULL;
1371         GSList *recipients = NULL;
1372
1373         if (msg == NULL)
1374                 return NULL;
1375
1376         header = tny_msg_get_header (msg);
1377         if (header == NULL)
1378                 return NULL;
1379
1380         recipients = modest_tny_msg_header_get_all_recipients_list (header);
1381         g_object_unref (header);
1382
1383         return recipients;
1384 }
1385