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