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