Fixes NB#140509, sync() currently viewed folder on send&receive even with auto-updates
[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         gchar *subject_prefix;
668         gboolean no_text_part;
669         gchar *parent_uid;
670         gboolean forward_as_attach = FALSE;
671
672         if (header)
673                 g_object_ref (header);
674         else
675                 header = tny_msg_get_header (msg);
676
677         /* Get body from original msg. Always look for the text/plain
678            part of the message to create the reply/forwarded mail */
679         if (msg != NULL) {
680                 body   = modest_tny_msg_find_body_part (msg, FALSE);
681                 html_body = modest_tny_msg_find_body_part (msg, TRUE);
682         }
683
684         if (modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT,
685                                   NULL))
686                 formatter = modest_formatter_new ("text/html", signature);
687         else
688                 formatter = modest_formatter_new ("text/plain", signature);
689
690
691         /* if we don't have a text-part */
692         no_text_part = (!body) || (strcmp (tny_mime_part_get_content_type (body), "text/html")==0);
693
694         /* when we're reply, include the text part if we have it, or nothing otherwise. */
695         if (is_reply)
696                 new_msg = modest_formatter_quote  (formatter, body, header,
697                                                     attachments);
698         else {
699                 if (no_text_part || (html_body && (strcmp (tny_mime_part_get_content_type (html_body), "text/html")==0))) {
700                         forward_as_attach = TRUE;
701                         new_msg = modest_formatter_attach (formatter, msg, header);
702                 } else {
703                         forward_as_attach = FALSE;
704                         new_msg = modest_formatter_inline  (formatter, body, header,
705                                                             attachments);
706                 }
707         }
708
709         g_object_unref (G_OBJECT(formatter));
710         if (body)
711                 g_object_unref (G_OBJECT(body));
712         if (html_body)
713                 g_object_unref (G_OBJECT(html_body));
714
715         /* Fill the header */
716         new_header = tny_msg_get_header (new_msg);
717         tny_header_set_from (new_header, from);
718         tny_header_set_replyto (new_header, from);
719
720         /* Change the subject */
721         if (is_reply)
722                 subject_prefix = g_strconcat (_("mail_va_re"), ":", NULL);
723         else
724                 subject_prefix = g_strconcat (_("mail_va_fw"), ":", NULL);
725         old_subject = tny_header_dup_subject (header);
726         new_subject =
727                 (gchar *) modest_text_utils_derived_subject (old_subject,
728                                                              subject_prefix);
729         g_free (old_subject);
730         g_free (subject_prefix);
731         tny_header_set_subject (new_header, (const gchar *) new_subject);
732         g_free (new_subject);
733
734         /* get the parent uid, and set it as a gobject property on the new msg */
735         parent_uid = modest_tny_folder_get_header_unique_id (header);
736         g_object_set_data_full (G_OBJECT(new_msg), MODEST_TNY_MSG_PARENT_UID,
737                                 parent_uid, g_free);
738
739         /* set modest as the X-Mailer
740          * we could this in the platform factory, but then the header
741          * would show up before all the others.
742          */
743         tny_mime_part_set_header_pair (TNY_MIME_PART (msg), "X-Mailer", "Modest "
744                                        VERSION);
745
746         /* Clean */
747         g_object_unref (G_OBJECT (new_header));
748         g_object_unref (G_OBJECT (header));
749         /* ugly to unref it here instead of in the calling func */
750
751         if (!is_reply & !forward_as_attach) {
752                 add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, NULL);
753         }
754
755         return new_msg;
756 }
757
758 const gchar*
759 modest_tny_msg_get_parent_uid (TnyMsg *msg)
760 {
761         g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
762         
763         return g_object_get_data (G_OBJECT(msg), MODEST_TNY_MSG_PARENT_UID);
764 }
765
766 static gchar *get_signed_protocol (TnyMimePart *part)
767 {
768         TnyList *header_pairs;
769         TnyIterator *iterator;
770         gchar *result = NULL;
771
772         header_pairs = TNY_LIST (tny_simple_list_new ());
773         tny_mime_part_get_header_pairs (part, header_pairs);
774         iterator = tny_list_create_iterator (header_pairs);
775
776         while (!result && !tny_iterator_is_done (iterator)) {
777                 TnyPair *pair;
778                 const gchar *name;
779
780                 pair = TNY_PAIR (tny_iterator_get_current (iterator));
781                 name = tny_pair_get_name (pair);
782                 if (name && !g_ascii_strcasecmp (name, "Content-Type")) {
783                         const gchar *s;
784                         s = tny_pair_get_value (pair);
785                         if (s) {
786                                 s = strstr (s, "protocol=");
787                                 if (s) {
788                                         const gchar *t;
789                                         s += 9;
790                                         if (*s == '\"') {
791                                                 s++;
792                                                 t = strstr (s, "\"");
793                                         } else {
794                                                 t = strstr (s, ";");
795                                         }
796                                         result = g_strndup (s, t - s);
797                                 }
798                         }
799                 }
800
801                 g_object_unref (pair);
802                 tny_iterator_next (iterator);
803         }
804
805         g_object_unref (iterator);
806         g_object_unref (header_pairs);
807
808         return result;
809 }
810
811 TnyMimePart *
812 modest_tny_msg_get_attachments_parent (TnyMsg *msg)
813 {
814         TnyMimePart *result;
815         const gchar *content_type;
816
817         result = NULL;
818
819         content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
820         if (content_type && !strcmp (content_type, "multipart/signed")) {
821                 TnyList *msg_children;
822                 TnyIterator *iterator;
823                 gchar *signed_protocol;
824
825                 msg_children = TNY_LIST (tny_simple_list_new ());
826                 tny_mime_part_get_parts (TNY_MIME_PART (msg), msg_children);
827
828                 iterator = tny_list_create_iterator (msg_children);
829                 signed_protocol = get_signed_protocol (TNY_MIME_PART (msg));
830                         
831                 while (!result && !tny_iterator_is_done (iterator)) {
832                         TnyMimePart *part;
833
834                         part = TNY_MIME_PART (tny_iterator_get_current (iterator));
835                         if (signed_protocol) {
836                                 const gchar *part_content_type;
837
838                                 part_content_type = tny_mime_part_get_content_type (part);
839                                 if (part_content_type && g_ascii_strcasecmp (part_content_type, signed_protocol)) {
840                                         result = g_object_ref (part);
841                                 }
842                         } else {
843                                 result = g_object_ref (part);
844                         }
845
846                         g_object_unref (part);
847                         tny_iterator_next (iterator);
848                 }
849
850                 g_object_unref (iterator);
851                 g_free (signed_protocol);
852                 g_object_unref (msg_children);
853         }
854         if (result == NULL) {
855                 result = g_object_ref (msg);
856         }
857
858         return result;
859 }
860
861
862
863 static void
864 add_if_attachment (gpointer data, gpointer user_data)
865 {
866         TnyMimePart *part;
867         GList **attachments_list;
868
869         part = TNY_MIME_PART (data);
870         attachments_list = ((GList **) user_data);
871
872         if ((tny_mime_part_is_attachment (part))||(TNY_IS_MSG (part))) {
873                 *attachments_list = g_list_prepend (*attachments_list, part);
874                 g_object_ref (part);
875         }
876 }
877
878 TnyMsg* 
879 modest_tny_msg_create_forward_msg (TnyMsg *msg, 
880                                    const gchar *from,
881                                    const gchar *signature,
882                                    ModestTnyMsgForwardType forward_type)
883 {
884         TnyMsg *new_msg;
885         TnyList *parts = NULL;
886         GList *attachments_list = NULL;
887         TnyMimePart *part_to_check = NULL;
888
889         g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
890
891         part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
892         
893         /* Add attachments */
894         parts = TNY_LIST (tny_simple_list_new());
895         tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
896         tny_list_foreach (parts, add_if_attachment, &attachments_list);
897
898         new_msg = create_reply_forward_mail (msg, NULL, from, signature, FALSE, forward_type,
899                                              attachments_list);
900
901         /* Clean */
902         if (attachments_list) {
903                 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
904                 g_list_free (attachments_list);
905         }
906         g_object_unref (G_OBJECT (parts));
907         g_object_unref (part_to_check);
908
909         return new_msg;
910 }
911
912
913
914 static gint
915 count_addresses (const gchar* addresses)
916 {
917         gint count = 1;
918
919         if (!addresses)
920                 return 0;
921         
922         while (*addresses) {
923                 if (*addresses == ',' || *addresses == ';')
924                         ++count;
925                 ++addresses;
926         }
927         
928         return count;
929 }
930
931 static void
932 remove_undisclosed_recipients (gchar **recipients)
933 {
934         GSList *addresses, *node;
935         gboolean is_first;
936         GString *result;
937
938         g_return_if_fail (recipients);
939         addresses = modest_text_utils_split_addresses_list (*recipients);
940
941         is_first = TRUE;
942         result = g_string_new ("");
943         for (node = addresses; node != NULL; node = g_slist_next (node)) {
944                 const gchar *address = (const gchar *) node->data;
945
946                 if (address && strstr (address, "undisclosed-recipients"))
947                         continue;
948
949                 if (is_first)
950                         is_first = FALSE;
951                 else
952                         result = g_string_append (result, ", ");
953
954                 result = g_string_append (result, address);
955         }
956         g_slist_foreach (addresses, (GFunc)g_free, NULL);
957         g_slist_free (addresses);
958
959         g_free (*recipients);
960         *recipients = g_string_free (result, FALSE);
961 }
962
963
964 /* get the new To:, based on the old header,
965  * result is newly allocated or NULL in case of error
966  * */
967 static gchar*
968 get_new_to (TnyMsg *msg, TnyHeader *header, const gchar* from,
969             ModestTnyMsgReplyMode reply_mode)
970 {
971         const gchar *reply_header = "Reply-To:";
972         const gchar *from_header = "From:";
973         gchar* old_reply_to;
974         gchar* old_from;
975         gchar* new_to;
976         gchar* tmp;
977         
978         /* according to RFC2369 (http://www.faqs.org/rfcs/rfc2369.html), we
979          * can identify Mailing-List posts by the List-Help header.
980          * for mailing lists, both the Reply-To: and From: should be included
981          * in the new To:; for now, we're ignoring List-Post
982          */
983         gchar* list_help = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg), 
984                                                                   "List-Help");
985         gboolean is_mailing_list = (list_help != NULL);
986         g_free (list_help);
987
988
989         /* reply to sender, use ReplyTo or From */
990         old_reply_to = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg), 
991                                                               "Reply-To"); 
992         old_from     = tny_header_dup_from (header);
993
994         if (!old_from && !old_reply_to) {
995                 g_debug ("%s: failed to get either Reply-To: or From: from header",
996                            __FUNCTION__);
997                 return NULL;
998         }
999
1000         /* Prevent DoS attacks caused by malformed emails */
1001         if (old_from) {
1002                 gchar *tmp = old_from;
1003                 old_from = modest_text_utils_get_secure_header ((const gchar *) tmp, from_header);
1004                 g_free (tmp);
1005         }
1006         if (old_reply_to) {
1007                 gchar *tmp = old_reply_to;
1008                 old_reply_to = modest_text_utils_get_secure_header ((const gchar *) tmp, reply_header);
1009                 g_free (tmp);
1010         }
1011
1012         /* for mailing lists, use both Reply-To and From if we did a
1013          * 'Reply All:'
1014          * */
1015         if (is_mailing_list && reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL &&
1016             old_reply_to && old_from && strcmp (old_from, old_reply_to) != 0)
1017                 new_to = g_strjoin (",", old_reply_to, old_from, NULL);
1018         else
1019                 /* otherwise use either Reply-To: (preferred) or From: */
1020                 new_to = g_strdup (old_reply_to ? old_reply_to : old_from);
1021         g_free (old_from);
1022         g_free (old_reply_to);
1023
1024         /* in case of ReplyAll, we need to add the Recipients in the old To: */
1025         if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1026                 gchar *old_to = tny_header_dup_to (header);
1027                 if (!old_to) 
1028                         g_debug ("%s: no To: address found in source mail",
1029                                    __FUNCTION__);
1030                 else {
1031                         /* append the old To: */
1032                         gchar *tmp = g_strjoin (",", new_to, old_to, NULL);
1033                         g_free (new_to);
1034                         new_to = tmp;
1035                         g_free (old_to);
1036                 }
1037
1038                 /* remove duplicate entries */
1039                 gchar *tmp = modest_text_utils_remove_duplicate_addresses (new_to);
1040                 g_free (new_to);
1041                 new_to = tmp;
1042                 
1043                 /* now, strip me (the new From:) from the new_to, but only if
1044                  * there are >1 addresses there */
1045                 if (count_addresses (new_to) > 1) {
1046                         gchar *tmp = modest_text_utils_remove_address (new_to, from);
1047                         g_free (new_to);
1048                         new_to = tmp;
1049                 }
1050         }
1051
1052         tmp = modest_text_utils_simplify_recipients (new_to);
1053         remove_undisclosed_recipients  (&tmp);
1054         g_free (new_to);
1055         new_to = tmp;
1056
1057         return new_to;
1058 }
1059
1060
1061 /* get the new Cc:, based on the old header,
1062  * result is newly allocated or NULL in case of error */
1063 static gchar*
1064 get_new_cc (TnyHeader *header, const gchar* from, const gchar *new_to)
1065 {
1066         gchar *old_cc, *result, *dup;
1067
1068         old_cc = tny_header_dup_cc (header);
1069         if (!old_cc)
1070                 return NULL;
1071
1072         /* remove me (the new From:) from the Cc: list */
1073         dup =  modest_text_utils_remove_address (old_cc, from);
1074
1075         if (new_to) {
1076                 gchar **to_parts, **current;
1077
1078                 to_parts = g_strsplit (new_to, ",", 0);
1079                 for (current = to_parts; current && *current != '\0'; current++) {
1080                         gchar *dup2;
1081
1082                         dup2 = modest_text_utils_remove_address (dup, g_strstrip (*current));
1083                         g_free (dup);
1084                         dup = dup2;
1085                 }
1086                 g_strfreev (to_parts);
1087         }
1088
1089         result = modest_text_utils_remove_duplicate_addresses (dup);
1090         g_free (dup);
1091         dup = result;
1092         result = modest_text_utils_simplify_recipients (dup);
1093         remove_undisclosed_recipients  (&result);
1094         g_free (dup);
1095         g_free (old_cc);
1096         return result;
1097 }
1098
1099 void 
1100 modest_tny_msg_get_references (TnyMsg *msg, gchar **message_id, gchar **references, gchar **in_reply_to)
1101 {
1102         TnyList *headers;
1103         TnyIterator *iterator;
1104         gchar *l_message_id;
1105         gchar *l_references;
1106         gchar *l_in_reply_to;
1107
1108         g_return_if_fail (TNY_IS_MSG (msg));
1109
1110         l_message_id = NULL;
1111         l_references = NULL;
1112         l_in_reply_to = NULL;
1113
1114         headers = TNY_LIST (tny_simple_list_new ());
1115         tny_mime_part_get_header_pairs (TNY_MIME_PART (msg), headers);
1116
1117         iterator = tny_list_create_iterator (headers);
1118         while (!tny_iterator_is_done (iterator)) {
1119                 TnyPair *pair;
1120                 const gchar *name;
1121
1122                 pair = TNY_PAIR (tny_iterator_get_current (iterator));
1123                 name = tny_pair_get_name (pair);
1124                 if (!g_ascii_strcasecmp (name, "References")) {
1125                         if (l_references) g_free (l_references);
1126                         l_references = g_strdup (tny_pair_get_value (pair));
1127                 } else if (!g_ascii_strcasecmp (name, "In-Reply-To")) {
1128                         if (l_in_reply_to) g_free (l_in_reply_to);
1129                         l_in_reply_to = g_strdup (tny_pair_get_value (pair));
1130                 } else if (!g_ascii_strcasecmp (name, "Message-ID")) {
1131                         if (l_message_id) g_free (l_message_id);
1132                         l_message_id = g_strdup (tny_pair_get_value (pair));
1133                 }
1134
1135                 g_object_unref (pair);
1136                 tny_iterator_next (iterator);
1137         }
1138
1139         g_object_unref (iterator);
1140         g_object_unref (headers);
1141
1142         if (message_id) {
1143                 *message_id = l_message_id;
1144         } else {
1145                 g_free (l_message_id);
1146         }
1147
1148         if (in_reply_to) {
1149                 *in_reply_to = l_in_reply_to;
1150         } else {
1151                 g_free (l_in_reply_to);
1152         }
1153
1154         if (references) {
1155                 *references = l_references;
1156         } else {
1157                 g_free (l_references);
1158         }
1159 }
1160
1161 static void 
1162 set_references (TnyMsg *reply_msg, TnyMsg *original_msg)
1163 {
1164         gchar *orig_references, *orig_in_reply_to, *orig_message_id;
1165         gchar *references, *in_reply_to;
1166
1167         modest_tny_msg_get_references (original_msg, &orig_message_id, &orig_references, &orig_in_reply_to);
1168
1169         references = NULL;
1170         in_reply_to = NULL;
1171
1172         if (orig_message_id)
1173                 in_reply_to = g_strdup (orig_message_id);
1174
1175         if (orig_references) {
1176                 if (orig_message_id)
1177                         references = g_strconcat (orig_references, "\n        ", orig_message_id, NULL);
1178                 else
1179                         references = g_strdup (orig_references);
1180
1181         } else if (orig_in_reply_to) {
1182                 if (orig_message_id)
1183                         references = g_strconcat (orig_in_reply_to, "\n        ", orig_message_id, NULL);
1184                 else
1185                         references = g_strdup (orig_in_reply_to);
1186         } else if (orig_message_id) {
1187                 references = g_strdup (orig_message_id);
1188         }
1189
1190         g_free (orig_references);
1191         g_free (orig_in_reply_to);
1192         g_free (orig_message_id);
1193
1194         if (in_reply_to) {
1195                 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "In-Reply-To", in_reply_to);
1196         }
1197         if (references) {
1198                 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "References", references);
1199         }
1200 }
1201
1202 TnyMsg* 
1203 modest_tny_msg_create_reply_msg (TnyMsg *msg,
1204                                  TnyHeader *header,
1205                                  const gchar *from,
1206                                  const gchar *signature,
1207                                  ModestTnyMsgReplyType reply_type,
1208                                  ModestTnyMsgReplyMode reply_mode)
1209 {
1210         TnyMsg *new_msg = NULL;
1211         TnyHeader *new_header;
1212         gchar *new_to = NULL;
1213         TnyList *parts = NULL;
1214         GList *attachments_list = NULL;
1215         TnyMimePart *part_to_check = NULL;
1216
1217         g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1218
1219         part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
1220
1221         parts = TNY_LIST (tny_simple_list_new());
1222         tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
1223         tny_list_foreach (parts, add_if_attachment, &attachments_list);
1224
1225         new_msg = create_reply_forward_mail (msg, header, from, signature, TRUE, reply_type,
1226                                              attachments_list);
1227
1228         set_references (new_msg, msg);
1229         if (attachments_list != NULL) {
1230                 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
1231                 g_list_free (attachments_list);
1232         }
1233         g_object_unref (parts);
1234
1235         /* Fill the header */
1236         if (header)
1237                 g_object_ref (header);
1238         else
1239                 header = tny_msg_get_header (msg);
1240
1241         
1242         new_header = tny_msg_get_header(new_msg);
1243         new_to = get_new_to (msg, header, from, reply_mode);
1244         if (!new_to)
1245                 g_debug ("%s: failed to get new To:", __FUNCTION__);
1246         else {
1247                 tny_header_set_to (new_header, new_to);
1248         }
1249
1250         if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1251                 gchar *new_cc = get_new_cc (header, from, new_to);
1252                 if (new_cc) { 
1253                         tny_header_set_cc (new_header, new_cc);
1254                         g_free (new_cc);
1255                 }
1256         }
1257
1258         if (new_to)
1259                 g_free (new_to);
1260
1261         /* Clean */
1262         g_object_unref (G_OBJECT (new_header));
1263         g_object_unref (G_OBJECT (header));
1264         g_object_unref (G_OBJECT (part_to_check));
1265
1266         return new_msg;
1267 }
1268
1269
1270 static gboolean
1271 is_ascii(const gchar *s)
1272 {
1273         if (!s)
1274                 return TRUE;
1275         while (s[0]) {
1276                 if (s[0] & 128 || s[0] < 32)
1277                         return FALSE;
1278                 s++;
1279         }
1280         return TRUE;
1281 }
1282
1283 static char *
1284 get_content_type(const gchar *s)
1285 {
1286         GString *type;
1287         
1288         type = g_string_new("text/plain");
1289         if (!is_ascii(s)) {
1290                 if (g_utf8_validate(s, -1, NULL)) {
1291                         g_string_append(type, "; charset=\"utf-8\"");
1292                 } else {
1293                         /* it should be impossible to reach this, but better safe than sorry */
1294                         g_debug("invalid utf8 in message");
1295                         g_string_append(type, "; charset=\"latin1\"");
1296                 }
1297         }
1298         return g_string_free(type, FALSE);
1299 }
1300
1301 guint64
1302 modest_tny_msg_estimate_size (const gchar *plain_body, const gchar *html_body,
1303                               guint64 parts_count,
1304                               guint64 parts_size)
1305 {
1306         guint64 result;
1307
1308         /* estimation of headers size */
1309         result = 1024;
1310
1311         /* We add a 20% of size due to the increase in 7bit encoding */
1312         if (plain_body) {
1313                 result += strlen (plain_body) * 120 / 100;
1314         }
1315         if (html_body) {
1316                 result += strlen (html_body) * 120 / 100;
1317         }
1318
1319         /* 256 bytes per additional part because of their headers */
1320         result += parts_count * 256;
1321
1322         /* 150% of increase per encoding */
1323         result += parts_size * 3 / 2;
1324
1325         return result;
1326 }
1327
1328 GSList *
1329 modest_tny_msg_header_get_all_recipients_list (TnyHeader *header)
1330 {
1331         GSList *recipients = NULL;
1332         gchar *from = NULL, *to = NULL, *cc = NULL, *bcc = NULL;
1333         gchar *after_remove;
1334         GString *buffer;
1335         gboolean add_separator = TRUE;
1336
1337         if (header == NULL)
1338                 return NULL;
1339
1340         buffer = g_string_new ("");
1341
1342         from = tny_header_dup_from (header);
1343         to = tny_header_dup_to (header);
1344         cc = tny_header_dup_cc (header);
1345         bcc = tny_header_dup_bcc (header);
1346
1347         recipients = NULL;
1348         if (from) {
1349                 buffer = g_string_append (buffer, from);
1350                 add_separator = TRUE;
1351         }
1352         if (to) {
1353                 if (add_separator)
1354                         buffer = g_string_append (buffer, "; ");
1355                 else
1356                         add_separator = TRUE;
1357
1358                 buffer = g_string_append (buffer, to);
1359         }
1360         if (cc) {
1361                 if (add_separator)
1362                         buffer = g_string_append (buffer, "; ");
1363                 else
1364                         add_separator = TRUE;
1365
1366                 buffer = g_string_append (buffer, cc);
1367         }
1368         if (bcc) {
1369                 if (add_separator)
1370                         buffer = g_string_append (buffer, "; ");
1371                 else
1372                         add_separator = TRUE;
1373
1374                 buffer = g_string_append (buffer, bcc);
1375         }
1376
1377         after_remove = modest_text_utils_remove_duplicate_addresses (buffer->str);
1378         g_string_free (buffer, TRUE);
1379
1380         recipients = modest_text_utils_split_addresses_list (after_remove);
1381         g_free (after_remove);
1382
1383         if (from)
1384                 g_free (from);
1385         if (to)
1386                 g_free (to);
1387         if (cc)
1388                 g_free (cc);
1389         if (bcc)
1390                 g_free (bcc);
1391
1392         return recipients;
1393 }
1394
1395 GSList *
1396 modest_tny_msg_get_all_recipients_list (TnyMsg *msg)
1397 {
1398         TnyHeader *header = NULL;
1399         GSList *recipients = NULL;
1400
1401         if (msg == NULL)
1402                 return NULL;
1403
1404         header = tny_msg_get_header (msg);
1405         if (header == NULL)
1406                 return NULL;
1407
1408         recipients = modest_tny_msg_header_get_all_recipients_list (header);
1409         g_object_unref (header);
1410
1411         return recipients;
1412 }
1413