1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
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>
48 #endif /*HAVE_CONFIG_H */
50 #include <modest-tny-msg.h>
51 #include "modest-text-utils.h"
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);
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,
67 GList *attachments, gint *attached, GError **err)
72 gint tmp_attached = 0;
75 new_msg = modest_formatter_create_message (NULL, TRUE, (attachments != NULL), FALSE);
76 header = tny_msg_get_header (new_msg);
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);
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);
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);
92 if ((subject != NULL) && (strlen(subject) > 0))
93 tny_header_set_subject (TNY_HEADER (header), subject);
95 content_type = get_content_type(body);
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.
101 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "X-Mailer", "Modest "
105 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "References", references);
108 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "In-Reply-To", in_reply_to);
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);
115 /* Add attachments */
117 tmp_attached = add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, err);
119 *attached = tmp_attached;
121 g_object_unref(header);
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)
139 new_msg = modest_formatter_create_message (NULL, FALSE, (attachments != NULL), (images != NULL));
140 header = tny_msg_get_header (new_msg);
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);
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);
153 if ((subject != NULL) && (strlen(subject) > 0))
154 tny_header_set_subject (TNY_HEADER (header), subject);
156 content_type = get_content_type(plain_body);
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.
162 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "X-Mailer", "Modest "
166 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "References", references);
169 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "In-Reply-To", in_reply_to);
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);
176 /* Add attachments */
177 tmp_attached = add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, err);
179 *attached = tmp_attached;
180 add_images (new_msg, images, err);
182 g_object_unref(header);
188 /* FIXME: this func copy from modest-mail-operation: refactor */
190 add_body_part (TnyMsg *msg,
192 const gchar *content_type)
194 TnyMimePart *text_body_part = NULL;
195 TnyStream *text_body_stream;
197 /* Create the stream */
198 text_body_stream = TNY_STREAM (tny_camel_mem_stream_new_with_buffer
199 (body, (body ? strlen(body) : 0)));
201 text_body_part = modest_formatter_create_body_part (NULL, msg);
203 /* Construct MIME part */
204 tny_stream_reset (text_body_stream);
205 tny_mime_part_construct (text_body_part,
207 content_type, "7bit");
208 tny_stream_reset (text_body_stream);
210 g_object_unref (G_OBJECT(text_body_part));
213 g_object_unref (text_body_stream);
215 return text_body_part;
219 add_html_body_part (TnyMsg *msg,
222 TnyMimePart *html_body_part = NULL;
223 TnyStream *html_body_stream;
225 /* Create the stream */
226 html_body_stream = TNY_STREAM (tny_camel_mem_stream_new_with_buffer
227 (body, (body) ? strlen(body) : 0));
229 /* Create body part if needed */
230 html_body_part = modest_formatter_create_body_part (NULL, msg);
232 /* Construct MIME part */
233 tny_stream_reset (html_body_stream);
234 tny_mime_part_construct (html_body_part,
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);
241 g_object_unref (G_OBJECT(html_body_part));
244 g_object_unref (html_body_stream);
246 return html_body_part;
250 copy_mime_part (TnyMimePart *part, GError **err)
252 TnyMimePart *result = NULL;
253 const gchar *attachment_content_type;
254 const gchar *attachment_filename;
255 const gchar *attachment_cid;
257 TnyIterator *iterator;
258 TnyStream *attachment_stream;
262 if (TNY_IS_MSG (part)) {
267 result = tny_platform_factory_new_mime_part (
268 modest_runtime_get_platform_factory());
270 attachment_content_type = tny_mime_part_get_content_type (part);
272 /* get mime part headers */
273 attachment_filename = tny_mime_part_get_filename (part);
274 attachment_cid = tny_mime_part_get_content_id (part);
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);
285 ret = tny_stream_reset (attachment_stream);
286 ret = tny_mime_part_construct (result,
288 attachment_content_type,
290 ret = tny_stream_reset (attachment_stream);
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);
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));
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);
310 tny_mime_part_set_content_id (result, subpart_cid);
311 g_object_unref (subpart_copy);
313 g_object_unref (subpart);
316 tny_iterator_next (iterator);
318 g_object_unref (iterator);
319 g_object_unref (parts);
320 g_object_unref (attachment_stream);
326 add_attachments (TnyMimePart *part, GList *attachments_list, gboolean add_inline, GError **err)
329 TnyMimePart *attachment_part, *old_attachment;
333 for (pos = (GList *)attachments_list; pos; pos = pos->next) {
335 old_attachment = pos->data;
336 if (!tny_mime_part_is_purged (old_attachment)) {
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) {
342 tny_mime_part_set_header_pair (attachment_part, "Content-Disposition",
345 const gchar *filename;
347 filename = tny_mime_part_get_filename (old_attachment);
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);
356 tny_mime_part_set_header_pair (attachment_part, "Content-Disposition",
360 if (!TNY_IS_MSG (old_attachment)) {
361 tny_mime_part_set_transfer_encoding (TNY_MIME_PART (attachment_part), "base64");
363 ret = tny_mime_part_add_part (TNY_MIME_PART (part), attachment_part);
366 tny_mime_part_set_content_id (attachment_part, old_cid);
367 g_object_unref (attachment_part);
376 add_images (TnyMsg *msg, GList *images_list, GError **err)
378 TnyMimePart *related_part = NULL;
379 const gchar *content_type;
381 content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
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);
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")) {
398 g_object_unref (part);
399 tny_iterator_next (iter);
401 g_object_unref (iter);
402 g_object_unref (parts);
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);
414 modest_tny_msg_get_body (TnyMsg *msg, gboolean want_html, gboolean *is_html)
419 GtkTextIter start, end;
421 gboolean result_was_html = TRUE;
423 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
425 body = modest_tny_msg_find_body_part(msg, want_html);
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);
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);
440 to_quote = to_quote_converted;
441 result_was_html = FALSE;
444 g_object_unref (buf);
445 g_object_unref (G_OBJECT(stream));
446 g_object_unref (G_OBJECT(body));
449 *is_html = result_was_html;
456 modest_tny_msg_find_body_part_in_alternative (TnyMimePart *msg, gboolean want_html)
460 TnyMimePart *part = NULL;
461 TnyMimePart *first_part = NULL;
462 const gchar *desired_mime_type = want_html ? "text/html" : "text/plain";
464 parts = TNY_LIST (tny_simple_list_new());
465 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
467 for (iter = tny_list_create_iterator(parts);
468 !tny_iterator_is_done (iter);
469 tny_iterator_next (iter)) {
473 part = TNY_MIME_PART (tny_iterator_get_current (iter));
475 if (first_part == NULL) {
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);
486 g_object_unref (part);
490 g_object_unref (iter);
491 g_object_unref (parts);
496 if (first_part) g_object_unref (first_part);
502 modest_tny_msg_find_body_part_from_mime_part (TnyMimePart *msg, gboolean want_html)
504 TnyMimePart *part = NULL;
505 TnyList *parts = NULL;
506 TnyIterator *iter = NULL;
507 gchar *header_content_type;
508 gchar *header_content_type_lower = NULL;
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);
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);
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);
534 g_free (header_content_type_lower);
535 g_free (header_content_type);
537 parts = TNY_LIST (tny_simple_list_new());
538 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
540 iter = tny_list_create_iterator(parts);
542 /* no parts? assume it's single-part message */
543 if (tny_iterator_is_done(iter)) {
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)
551 g_str_has_prefix (content_type, "text/");
552 g_free (content_type);
553 /* if this part cannot be a supported body return NULL */
557 return TNY_MIME_PART (g_object_ref(G_OBJECT(msg)));
561 gchar *tmp, *content_type = NULL;
562 gboolean has_content_disp_name = FALSE;
564 part = TNY_MIME_PART(tny_iterator_get_current (iter));
567 g_warning ("%s: not a valid mime part", __FUNCTION__);
568 tny_iterator_next (iter);
572 /* it's a message --> ignore */
573 if (part && TNY_IS_MSG (part)) {
574 g_object_unref (part);
576 tny_iterator_next (iter);
580 /* we need to strdown the content type, because
581 * tny_mime_part_has_content_type does not do it...
583 content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
585 /* mime-parts with a content-disposition header (either 'inline' or 'attachment')
586 * and a 'name=' thingy cannot be body parts
589 tmp = modest_tny_mime_part_get_header_value (part, "Content-Disposition");
591 gchar *content_disp = g_ascii_strdown(tmp, -1);
593 has_content_disp_name = g_strstr_len (content_disp, strlen(content_disp), "name=") != NULL;
594 g_free (content_disp);
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);
605 } else if (g_str_has_prefix(content_type, "multipart/alternative")) {
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);
614 } else if (g_str_has_prefix(content_type, "multipart")) {
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);
623 g_free (content_type);
626 g_object_unref (G_OBJECT(part));
630 tny_iterator_next (iter);
632 } while (!tny_iterator_is_done(iter));
635 g_object_unref (G_OBJECT(iter));
636 g_object_unref (G_OBJECT(parts));
638 return part; /* this maybe NULL, this is not an error; some message just don't have a body
643 modest_tny_msg_find_calendar_from_mime_part (TnyMimePart *msg)
645 TnyMimePart *part = NULL;
646 TnyList *parts = NULL;
647 TnyIterator *iter = NULL;
648 gchar *header_content_type;
649 gchar *header_content_type_lower = NULL;
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);
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);
669 g_free (header_content_type_lower);
670 g_free (header_content_type);
672 parts = TNY_LIST (tny_simple_list_new());
673 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
675 iter = tny_list_create_iterator(parts);
677 /* no parts? assume it's single-part message */
678 if (tny_iterator_is_done(iter)) {
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)
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) {
692 return TNY_MIME_PART (g_object_ref(G_OBJECT(msg)));
696 gchar *tmp, *content_type = NULL;
697 gboolean has_content_disp_name = FALSE;
699 part = TNY_MIME_PART(tny_iterator_get_current (iter));
702 g_warning ("%s: not a valid mime part", __FUNCTION__);
703 tny_iterator_next (iter);
707 /* it's a message --> ignore */
708 if (part && TNY_IS_MSG (part)) {
709 g_object_unref (part);
711 tny_iterator_next (iter);
715 /* we need to strdown the content type, because
716 * tny_mime_part_has_content_type does not do it...
718 content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
720 /* mime-parts with a content-disposition header (either 'inline' or 'attachment')
721 * and a 'name=' thingy cannot be body parts
724 tmp = modest_tny_mime_part_get_header_value (part, "Content-Disposition");
726 gchar *content_disp = g_ascii_strdown(tmp, -1);
728 has_content_disp_name = g_strstr_len (content_disp, strlen(content_disp), "name=") != NULL;
729 g_free (content_disp);
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);
740 } else if (g_str_has_prefix(content_type, "multipart")) {
742 /* multipart? recurse! */
743 g_object_unref (part);
744 g_free (content_type);
745 part = modest_tny_msg_find_calendar_from_mime_part (part);
749 g_free (content_type);
752 g_object_unref (G_OBJECT(part));
756 tny_iterator_next (iter);
758 } while (!tny_iterator_is_done(iter));
761 g_object_unref (G_OBJECT(iter));
762 g_object_unref (G_OBJECT(parts));
764 return part; /* this maybe NULL, this is not an error; some message just don't have a body
770 modest_tny_msg_find_body_part (TnyMsg *msg, gboolean want_html)
772 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
774 return modest_tny_msg_find_body_part_from_mime_part (TNY_MIME_PART(msg),
779 modest_tny_msg_find_calendar (TnyMsg *msg)
781 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
783 return modest_tny_msg_find_calendar_from_mime_part (TNY_MIME_PART(msg));
787 #define MODEST_TNY_MSG_PARENT_UID "parent-uid"
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)
795 TnyHeader *new_header;
798 TnyMimePart *body = NULL;
799 TnyMimePart *html_body = NULL;
800 ModestFormatter *formatter;
801 gboolean no_text_part;
803 gboolean forward_as_attach = FALSE;
806 g_object_ref (header);
808 header = tny_msg_get_header (msg);
810 /* Get body from original msg. Always look for the text/plain
811 part of the message to create the reply/forwarded mail */
813 body = modest_tny_msg_find_body_part (msg, FALSE);
814 html_body = modest_tny_msg_find_body_part (msg, TRUE);
817 if (modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT,
819 formatter = modest_formatter_new ("text/html", signature);
821 formatter = modest_formatter_new ("text/plain", signature);
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);
827 /* when we're reply, include the text part if we have it, or nothing otherwise. */
829 new_msg = modest_formatter_quote (formatter, body, header,
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);
836 forward_as_attach = FALSE;
837 new_msg = modest_formatter_inline (formatter, body, header,
842 g_object_unref (G_OBJECT(formatter));
844 g_object_unref (G_OBJECT(body));
846 g_object_unref (G_OBJECT(html_body));
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);
853 /* Change the subject */
854 old_subject = tny_header_dup_subject (header);
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);
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,
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.
870 tny_mime_part_set_header_pair (TNY_MIME_PART (msg), "X-Mailer", "Modest "
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 */
878 if (!is_reply & !forward_as_attach) {
879 add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, NULL);
886 modest_tny_msg_get_parent_uid (TnyMsg *msg)
888 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
890 return g_object_get_data (G_OBJECT(msg), MODEST_TNY_MSG_PARENT_UID);
893 static gchar *get_signed_protocol (TnyMimePart *part)
895 TnyList *header_pairs;
896 TnyIterator *iterator;
897 gchar *result = NULL;
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);
903 while (!result && !tny_iterator_is_done (iterator)) {
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")) {
911 s = tny_pair_get_value (pair);
913 s = strstr (s, "protocol=");
919 t = strstr (s, "\"");
923 result = g_strndup (s, t - s);
928 g_object_unref (pair);
929 tny_iterator_next (iterator);
932 g_object_unref (iterator);
933 g_object_unref (header_pairs);
939 modest_tny_msg_get_attachments_parent (TnyMsg *msg)
942 const gchar *content_type;
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;
952 msg_children = TNY_LIST (tny_simple_list_new ());
953 tny_mime_part_get_parts (TNY_MIME_PART (msg), msg_children);
955 iterator = tny_list_create_iterator (msg_children);
956 signed_protocol = get_signed_protocol (TNY_MIME_PART (msg));
958 while (!result && !tny_iterator_is_done (iterator)) {
961 part = TNY_MIME_PART (tny_iterator_get_current (iterator));
962 if (signed_protocol) {
963 const gchar *part_content_type;
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);
970 result = g_object_ref (part);
973 g_object_unref (part);
974 tny_iterator_next (iterator);
977 g_object_unref (iterator);
978 g_free (signed_protocol);
979 g_object_unref (msg_children);
981 if (result == NULL) {
982 result = g_object_ref (msg);
991 add_if_attachment (gpointer data, gpointer user_data)
994 GList **attachments_list;
996 part = TNY_MIME_PART (data);
997 attachments_list = ((GList **) user_data);
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);
1006 modest_tny_msg_create_forward_msg (TnyMsg *msg,
1008 const gchar *signature,
1009 ModestTnyMsgForwardType forward_type)
1012 TnyList *parts = NULL;
1013 GList *attachments_list = NULL;
1014 TnyMimePart *part_to_check = NULL;
1016 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1018 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
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);
1025 new_msg = create_reply_forward_mail (msg, NULL, from, signature, FALSE, forward_type,
1029 if (attachments_list) {
1030 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
1031 g_list_free (attachments_list);
1033 g_object_unref (G_OBJECT (parts));
1034 g_object_unref (part_to_check);
1042 count_addresses (const gchar* addresses)
1049 while (*addresses) {
1050 if (*addresses == ',' || *addresses == ';')
1059 remove_undisclosed_recipients (gchar **recipients)
1061 GSList *addresses, *node;
1065 g_return_if_fail (recipients);
1066 addresses = modest_text_utils_split_addresses_list (*recipients);
1069 result = g_string_new ("");
1070 for (node = addresses; node != NULL; node = g_slist_next (node)) {
1071 const gchar *address = (const gchar *) node->data;
1073 if (address && strstr (address, "undisclosed-recipients"))
1079 result = g_string_append (result, ", ");
1081 result = g_string_append (result, address);
1083 g_slist_foreach (addresses, (GFunc)g_free, NULL);
1084 g_slist_free (addresses);
1086 g_free (*recipients);
1087 *recipients = g_string_free (result, FALSE);
1091 /* get the new To:, based on the old header,
1092 * result is newly allocated or NULL in case of error
1095 get_new_to (TnyMsg *msg, TnyHeader *header, const gchar* from,
1096 ModestTnyMsgReplyMode reply_mode)
1098 const gchar *reply_header = "Reply-To:";
1099 const gchar *from_header = "From:";
1100 gchar* old_reply_to;
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
1110 gchar* list_help = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
1112 gboolean is_mailing_list = (list_help != NULL);
1116 /* reply to sender, use ReplyTo or From */
1117 old_reply_to = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
1119 old_from = tny_header_dup_from (header);
1121 if (!old_from && !old_reply_to) {
1122 g_debug ("%s: failed to get either Reply-To: or From: from header",
1127 /* Prevent DoS attacks caused by malformed emails */
1129 gchar *tmp = old_from;
1130 old_from = modest_text_utils_get_secure_header ((const gchar *) tmp, from_header);
1134 gchar *tmp = old_reply_to;
1135 old_reply_to = modest_text_utils_get_secure_header ((const gchar *) tmp, reply_header);
1139 /* for mailing lists, use both Reply-To and From if we did a
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);
1146 /* otherwise use either Reply-To: (preferred) or From: */
1147 new_to = g_strdup (old_reply_to ? old_reply_to : old_from);
1149 g_free (old_reply_to);
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);
1155 g_debug ("%s: no To: address found in source mail",
1158 /* append the old To: */
1159 gchar *tmp = g_strjoin (",", new_to, old_to, NULL);
1165 /* remove duplicate entries */
1166 gchar *tmp = modest_text_utils_remove_duplicate_addresses (new_to);
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);
1179 tmp = modest_text_utils_simplify_recipients (new_to);
1180 remove_undisclosed_recipients (&tmp);
1188 /* get the new Cc:, based on the old header,
1189 * result is newly allocated or NULL in case of error */
1191 get_new_cc (TnyHeader *header, const gchar* from, const gchar *new_to)
1193 gchar *old_cc, *result, *dup;
1195 old_cc = tny_header_dup_cc (header);
1199 /* remove me (the new From:) from the Cc: list */
1200 dup = modest_text_utils_remove_address (old_cc, from);
1203 gchar **to_parts, **current;
1205 to_parts = g_strsplit (new_to, ",", 0);
1206 for (current = to_parts; current && *current != '\0'; current++) {
1209 dup2 = modest_text_utils_remove_address (dup, g_strstrip (*current));
1213 g_strfreev (to_parts);
1216 result = modest_text_utils_remove_duplicate_addresses (dup);
1219 result = modest_text_utils_simplify_recipients (dup);
1220 remove_undisclosed_recipients (&result);
1227 modest_tny_msg_get_references (TnyMsg *msg, gchar **message_id, gchar **references, gchar **in_reply_to)
1230 TnyIterator *iterator;
1231 gchar *l_message_id;
1232 gchar *l_references;
1233 gchar *l_in_reply_to;
1235 g_return_if_fail (TNY_IS_MSG (msg));
1237 l_message_id = NULL;
1238 l_references = NULL;
1239 l_in_reply_to = NULL;
1241 headers = TNY_LIST (tny_simple_list_new ());
1242 tny_mime_part_get_header_pairs (TNY_MIME_PART (msg), headers);
1244 iterator = tny_list_create_iterator (headers);
1245 while (!tny_iterator_is_done (iterator)) {
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));
1262 g_object_unref (pair);
1263 tny_iterator_next (iterator);
1266 g_object_unref (iterator);
1267 g_object_unref (headers);
1270 *message_id = l_message_id;
1272 g_free (l_message_id);
1276 *in_reply_to = l_in_reply_to;
1278 g_free (l_in_reply_to);
1282 *references = l_references;
1284 g_free (l_references);
1289 remove_line_breaks (gchar *str)
1291 gchar *needle = g_strrstr (str, "\r\n");
1297 set_references (TnyMsg *reply_msg, TnyMsg *original_msg)
1299 gchar *orig_references, *orig_in_reply_to, *orig_message_id;
1300 gchar *references, *in_reply_to;
1302 modest_tny_msg_get_references (original_msg, &orig_message_id, &orig_references, &orig_in_reply_to);
1307 if (orig_message_id)
1308 in_reply_to = g_strdup (orig_message_id);
1310 if (orig_references) {
1311 if (orig_message_id)
1312 references = g_strconcat (orig_references, "\n ", orig_message_id, NULL);
1314 references = g_strdup (orig_references);
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);
1320 references = g_strdup (orig_in_reply_to);
1321 } else if (orig_message_id) {
1322 references = g_strdup (orig_message_id);
1325 g_free (orig_references);
1326 g_free (orig_in_reply_to);
1327 g_free (orig_message_id);
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);
1335 remove_line_breaks (references);
1336 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "References", references);
1337 g_free (references);
1342 modest_tny_msg_create_reply_msg (TnyMsg *msg,
1345 const gchar *signature,
1346 ModestTnyMsgReplyType reply_type,
1347 ModestTnyMsgReplyMode reply_mode)
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;
1356 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1358 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
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);
1364 new_msg = create_reply_forward_mail (msg, header, from, signature, TRUE, reply_type,
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);
1372 g_object_unref (parts);
1374 /* Fill the header */
1376 g_object_ref (header);
1378 header = tny_msg_get_header (msg);
1381 new_header = tny_msg_get_header(new_msg);
1382 new_to = get_new_to (msg, header, from, reply_mode);
1384 g_debug ("%s: failed to get new To:", __FUNCTION__);
1386 tny_header_set_to (new_header, new_to);
1389 if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1390 gchar *new_cc = get_new_cc (header, from, new_to);
1392 tny_header_set_cc (new_header, new_cc);
1401 g_object_unref (G_OBJECT (new_header));
1402 g_object_unref (G_OBJECT (header));
1403 g_object_unref (G_OBJECT (part_to_check));
1409 modest_tny_msg_create_reply_calendar_msg (TnyMsg *msg,
1412 const gchar *signature,
1415 TnyMsg *new_msg = NULL;
1416 TnyIterator *iterator;
1418 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
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);
1423 iterator = tny_list_create_iterator (headers);
1424 while (!tny_iterator_is_done (iterator)) {
1425 TnyPair *pair = TNY_PAIR (tny_iterator_get_current (iterator));
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);
1433 g_object_unref (iterator);
1440 is_ascii(const gchar *s)
1445 if (s[0] & 128 || s[0] < 32)
1453 get_content_type(const gchar *s)
1457 type = g_string_new("text/plain");
1459 if (g_utf8_validate(s, -1, NULL)) {
1460 g_string_append(type, "; charset=\"utf-8\"");
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\"");
1467 return g_string_free(type, FALSE);
1471 modest_tny_msg_estimate_size (const gchar *plain_body, const gchar *html_body,
1472 guint64 parts_count,
1477 /* estimation of headers size */
1480 /* We add a 20% of size due to the increase in 7bit encoding */
1482 result += strlen (plain_body) * 120 / 100;
1485 result += strlen (html_body) * 120 / 100;
1488 /* 256 bytes per additional part because of their headers */
1489 result += parts_count * 256;
1491 /* 150% of increase per encoding */
1492 result += parts_size * 3 / 2;
1498 modest_tny_msg_header_get_all_recipients_list (TnyHeader *header)
1500 GSList *recipients = NULL;
1501 gchar *from = NULL, *to = NULL, *cc = NULL, *bcc = NULL;
1502 gchar *after_remove, *joined;
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);
1512 joined = modest_text_utils_join_addresses (from, to, cc, bcc);
1513 after_remove = modest_text_utils_remove_duplicate_addresses (joined);
1516 recipients = modest_text_utils_split_addresses_list (after_remove);
1517 g_free (after_remove);
1532 modest_tny_msg_get_all_recipients_list (TnyMsg *msg)
1534 TnyHeader *header = NULL;
1535 GSList *recipients = NULL;
1540 header = tny_msg_get_header (msg);
1544 recipients = modest_tny_msg_header_get_all_recipients_list (header);
1545 g_object_unref (header);