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