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);
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);
65 add_header_pairs (TnyMimePart *part, TnyList *header_pairs)
67 TnyIterator *iterator;
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);
76 g_object_unref (iterator);
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,
84 GList *attachments, gint *attached, TnyList *header_pairs, GError **err)
89 gint tmp_attached = 0;
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);
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);
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);
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);
110 if ((subject != NULL) && (strlen(subject) > 0))
111 tny_header_set_subject (TNY_HEADER (header), subject);
113 content_type = get_content_type(body);
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.
119 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "X-Mailer", "Modest "
123 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "References", references);
126 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "In-Reply-To", in_reply_to);
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);
133 /* Add attachments */
135 tmp_attached = add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, err);
137 *attached = tmp_attached;
139 g_object_unref(header);
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)
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);
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);
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);
172 if ((subject != NULL) && (strlen(subject) > 0))
173 tny_header_set_subject (TNY_HEADER (header), subject);
175 content_type = get_content_type(plain_body);
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.
181 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "X-Mailer", "Modest "
185 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "References", references);
188 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "In-Reply-To", in_reply_to);
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);
195 /* Add attachments */
196 tmp_attached = add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, err);
198 *attached = tmp_attached;
199 add_images (new_msg, images, err);
201 g_object_unref(header);
207 /* FIXME: this func copy from modest-mail-operation: refactor */
209 add_body_part (TnyMsg *msg,
211 const gchar *content_type)
213 TnyMimePart *text_body_part = NULL;
214 TnyStream *text_body_stream;
216 /* Create the stream */
217 text_body_stream = TNY_STREAM (tny_camel_mem_stream_new_with_buffer
218 (body, (body ? strlen(body) : 0)));
220 text_body_part = modest_formatter_create_body_part (NULL, msg);
222 /* Construct MIME part */
223 tny_stream_reset (text_body_stream);
224 tny_mime_part_construct (text_body_part,
226 content_type, "7bit");
227 tny_stream_reset (text_body_stream);
229 g_object_unref (G_OBJECT(text_body_part));
232 g_object_unref (text_body_stream);
234 return text_body_part;
238 add_html_body_part (TnyMsg *msg,
241 TnyMimePart *html_body_part = NULL;
242 TnyStream *html_body_stream;
244 /* Create the stream */
245 html_body_stream = TNY_STREAM (tny_camel_mem_stream_new_with_buffer
246 (body, (body) ? strlen(body) : 0));
248 /* Create body part if needed */
249 html_body_part = modest_formatter_create_body_part (NULL, msg);
251 /* Construct MIME part */
252 tny_stream_reset (html_body_stream);
253 tny_mime_part_construct (html_body_part,
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);
260 g_object_unref (G_OBJECT(html_body_part));
263 g_object_unref (html_body_stream);
265 return html_body_part;
269 copy_mime_part (TnyMimePart *part, GError **err)
271 TnyMimePart *result = NULL;
272 const gchar *attachment_content_type;
273 const gchar *attachment_filename;
274 const gchar *attachment_cid;
276 TnyIterator *iterator;
277 TnyStream *attachment_stream;
281 if (TNY_IS_MSG (part)) {
286 result = tny_platform_factory_new_mime_part (
287 modest_runtime_get_platform_factory());
289 attachment_content_type = tny_mime_part_get_content_type (part);
291 /* get mime part headers */
292 attachment_filename = tny_mime_part_get_filename (part);
293 attachment_cid = tny_mime_part_get_content_id (part);
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);
304 ret = tny_stream_reset (attachment_stream);
305 ret = tny_mime_part_construct (result,
307 attachment_content_type,
309 ret = tny_stream_reset (attachment_stream);
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);
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));
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);
329 tny_mime_part_set_content_id (result, subpart_cid);
330 g_object_unref (subpart_copy);
332 g_object_unref (subpart);
335 tny_iterator_next (iterator);
337 g_object_unref (iterator);
338 g_object_unref (parts);
339 g_object_unref (attachment_stream);
345 add_attachments (TnyMimePart *part, GList *attachments_list, gboolean add_inline, GError **err)
348 TnyMimePart *attachment_part, *old_attachment;
352 for (pos = (GList *)attachments_list; pos; pos = pos->next) {
354 old_attachment = pos->data;
355 if (!tny_mime_part_is_purged (old_attachment)) {
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) {
361 tny_mime_part_set_header_pair (attachment_part, "Content-Disposition",
364 const gchar *filename;
366 filename = tny_mime_part_get_filename (old_attachment);
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);
375 tny_mime_part_set_header_pair (attachment_part, "Content-Disposition",
379 if (!TNY_IS_MSG (old_attachment)) {
380 tny_mime_part_set_transfer_encoding (TNY_MIME_PART (attachment_part), "base64");
382 ret = tny_mime_part_add_part (TNY_MIME_PART (part), attachment_part);
385 tny_mime_part_set_content_id (attachment_part, old_cid);
386 g_object_unref (attachment_part);
395 add_images (TnyMsg *msg, GList *images_list, GError **err)
397 TnyMimePart *related_part = NULL;
398 const gchar *content_type;
400 content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
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);
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")) {
417 g_object_unref (part);
418 tny_iterator_next (iter);
420 g_object_unref (iter);
421 g_object_unref (parts);
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);
433 modest_tny_msg_get_body (TnyMsg *msg, gboolean want_html, gboolean *is_html)
438 GtkTextIter start, end;
440 gboolean result_was_html = TRUE;
442 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
444 body = modest_tny_msg_find_body_part(msg, want_html);
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);
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);
459 to_quote = to_quote_converted;
460 result_was_html = FALSE;
463 g_object_unref (buf);
464 g_object_unref (G_OBJECT(stream));
465 g_object_unref (G_OBJECT(body));
468 *is_html = result_was_html;
475 modest_tny_msg_find_body_part_in_alternative (TnyMimePart *msg, gboolean want_html)
479 TnyMimePart *part = NULL, *related = NULL;
480 TnyMimePart *first_part = NULL;
481 const gchar *desired_mime_type = want_html ? "text/html" : "text/plain";
483 parts = TNY_LIST (tny_simple_list_new());
484 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
486 for (iter = tny_list_create_iterator(parts);
487 !tny_iterator_is_done (iter);
488 tny_iterator_next (iter)) {
492 part = TNY_MIME_PART (tny_iterator_get_current (iter));
494 if (first_part == NULL) {
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);
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
511 g_object_unref (related);
512 related = g_object_ref (part);
515 g_object_unref (part);
519 g_object_unref (iter);
520 g_object_unref (parts);
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);
535 g_object_unref (first_part);
537 g_object_unref (related);
543 modest_tny_msg_find_body_part_from_mime_part (TnyMimePart *msg, gboolean want_html)
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;
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);
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);
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);
577 if (header_content_type_lower &&
578 g_str_has_prefix (header_content_type_lower, "multipart/related"))
581 g_free (header_content_type_lower);
582 g_free (header_content_type);
584 parts = TNY_LIST (tny_simple_list_new());
585 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
587 /* no parts? assume it's single-part message */
588 iter = tny_list_create_iterator (parts);
589 if (tny_iterator_is_done(iter)) {
591 gboolean is_text_part;
593 content_type = modest_tny_mime_part_get_content_type (msg);
594 if (content_type == NULL)
596 is_text_part = g_str_has_prefix (content_type, "text/");
597 g_free (content_type);
599 /* if this part cannot be a supported body return NULL */
601 part = TNY_MIME_PART (g_object_ref(G_OBJECT(msg)));
603 TnyMimePart *fallback = NULL;
605 gchar *tmp, *content_type = NULL;
606 gboolean has_content_disp_name = FALSE;
608 part = TNY_MIME_PART(tny_iterator_get_current (iter));
611 g_warning ("%s: not a valid mime part", __FUNCTION__);
612 tny_iterator_next (iter);
616 /* it's a message --> ignore */
617 if (part && TNY_IS_MSG (part)) {
618 g_object_unref (part);
620 tny_iterator_next (iter);
624 /* we need to strdown the content type, because
625 * tny_mime_part_has_content_type does not do it...
627 content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
629 /* mime-parts with a content-disposition header (either 'inline' or 'attachment')
630 * and a 'name=' thingy cannot be body parts
633 tmp = modest_tny_mime_part_get_header_value (part, "Content-Disposition");
635 gchar *content_disp = g_ascii_strdown(tmp, -1);
637 has_content_disp_name = g_strstr_len (content_disp, strlen(content_disp), "name=") != NULL;
638 g_free (content_disp);
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);
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);
655 } else if (g_str_has_prefix(content_type, "multipart/alternative")) {
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);
664 } else if (g_str_has_prefix(content_type, "multipart")) {
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);
673 g_free (content_type);
677 g_object_unref (G_OBJECT(part));
680 tny_iterator_next (iter);
682 } while (!tny_iterator_is_done(iter));
684 if (!part && fallback)
685 part = g_object_ref (fallback);
687 g_object_unref (fallback);
691 g_object_unref (G_OBJECT(iter));
692 g_object_unref (G_OBJECT(parts));
698 modest_tny_msg_find_calendar_from_mime_part (TnyMimePart *msg)
700 TnyMimePart *part = NULL;
701 TnyList *parts = NULL;
702 TnyIterator *iter = NULL;
703 gchar *header_content_type;
704 gchar *header_content_type_lower = NULL;
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);
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);
724 g_free (header_content_type_lower);
725 g_free (header_content_type);
727 parts = TNY_LIST (tny_simple_list_new());
728 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
730 iter = tny_list_create_iterator(parts);
732 /* no parts? assume it's single-part message */
733 if (tny_iterator_is_done(iter)) {
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)
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) {
747 return TNY_MIME_PART (g_object_ref(G_OBJECT(msg)));
751 gchar *tmp, *content_type = NULL;
752 gboolean has_content_disp_name = FALSE;
754 part = TNY_MIME_PART(tny_iterator_get_current (iter));
757 g_warning ("%s: not a valid mime part", __FUNCTION__);
758 tny_iterator_next (iter);
762 /* it's a message --> ignore */
763 if (part && TNY_IS_MSG (part)) {
764 g_object_unref (part);
766 tny_iterator_next (iter);
770 /* we need to strdown the content type, because
771 * tny_mime_part_has_content_type does not do it...
773 content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
775 /* mime-parts with a content-disposition header (either 'inline' or 'attachment')
776 * and a 'name=' thingy cannot be body parts
779 tmp = modest_tny_mime_part_get_header_value (part, "Content-Disposition");
781 gchar *content_disp = g_ascii_strdown(tmp, -1);
783 has_content_disp_name = g_strstr_len (content_disp, strlen(content_disp), "name=") != NULL;
784 g_free (content_disp);
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);
795 } else if (g_str_has_prefix(content_type, "multipart")) {
797 /* multipart? recurse! */
798 g_object_unref (part);
799 g_free (content_type);
800 part = modest_tny_msg_find_calendar_from_mime_part (part);
804 g_free (content_type);
807 g_object_unref (G_OBJECT(part));
811 tny_iterator_next (iter);
813 } while (!tny_iterator_is_done(iter));
816 g_object_unref (G_OBJECT(iter));
817 g_object_unref (G_OBJECT(parts));
819 return part; /* this maybe NULL, this is not an error; some message just don't have a body
825 modest_tny_msg_find_body_part (TnyMsg *msg, gboolean want_html)
827 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
829 return modest_tny_msg_find_body_part_from_mime_part (TNY_MIME_PART(msg),
834 modest_tny_msg_find_calendar (TnyMsg *msg)
836 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
838 return modest_tny_msg_find_calendar_from_mime_part (TNY_MIME_PART(msg));
842 #define MODEST_TNY_MSG_PARENT_UID "parent-uid"
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)
850 TnyHeader *new_header;
853 TnyMimePart *body = NULL;
854 TnyMimePart *html_body = NULL;
855 ModestFormatter *formatter;
856 gboolean no_text_part;
858 gboolean forward_as_attach = FALSE;
861 g_object_ref (header);
863 header = tny_msg_get_header (msg);
865 /* Get body from original msg. Always look for the text/plain
866 part of the message to create the reply/forwarded mail */
868 body = modest_tny_msg_find_body_part (msg, FALSE);
869 html_body = modest_tny_msg_find_body_part (msg, TRUE);
872 if (modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT,
874 formatter = modest_formatter_new ("text/html", signature);
876 formatter = modest_formatter_new ("text/plain", signature);
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);
882 /* when we're reply, include the text part if we have it, or nothing otherwise. */
884 new_msg = modest_formatter_quote (formatter, body, header,
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);
891 forward_as_attach = FALSE;
892 new_msg = modest_formatter_inline (formatter, body, header,
897 g_object_unref (G_OBJECT(formatter));
899 g_object_unref (G_OBJECT(body));
901 g_object_unref (G_OBJECT(html_body));
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);
908 /* Change the subject */
909 old_subject = tny_header_dup_subject (header);
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);
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,
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.
925 tny_mime_part_set_header_pair (TNY_MIME_PART (msg), "X-Mailer", "Modest "
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 */
933 if (!is_reply & !forward_as_attach) {
934 add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, NULL);
941 modest_tny_msg_get_parent_uid (TnyMsg *msg)
943 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
945 return g_object_get_data (G_OBJECT(msg), MODEST_TNY_MSG_PARENT_UID);
948 static gchar *get_signed_protocol (TnyMimePart *part)
950 TnyList *header_pairs;
951 TnyIterator *iterator;
952 gchar *result = NULL;
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);
958 while (!result && !tny_iterator_is_done (iterator)) {
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")) {
966 s = tny_pair_get_value (pair);
968 s = strstr (s, "protocol=");
974 t = strstr (s, "\"");
978 result = g_strndup (s, t - s);
983 g_object_unref (pair);
984 tny_iterator_next (iterator);
987 g_object_unref (iterator);
988 g_object_unref (header_pairs);
994 modest_tny_msg_get_attachments_parent (TnyMsg *msg)
997 const gchar *content_type;
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;
1007 msg_children = TNY_LIST (tny_simple_list_new ());
1008 tny_mime_part_get_parts (TNY_MIME_PART (msg), msg_children);
1010 iterator = tny_list_create_iterator (msg_children);
1011 signed_protocol = get_signed_protocol (TNY_MIME_PART (msg));
1013 while (!result && !tny_iterator_is_done (iterator)) {
1016 part = TNY_MIME_PART (tny_iterator_get_current (iterator));
1017 if (signed_protocol) {
1018 const gchar *part_content_type;
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);
1025 result = g_object_ref (part);
1028 g_object_unref (part);
1029 tny_iterator_next (iterator);
1032 g_object_unref (iterator);
1033 g_free (signed_protocol);
1034 g_object_unref (msg_children);
1036 if (result == NULL) {
1037 result = g_object_ref (msg);
1046 add_if_attachment (gpointer data, gpointer user_data)
1049 GList **attachments_list;
1051 part = TNY_MIME_PART (data);
1052 attachments_list = ((GList **) user_data);
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);
1061 modest_tny_msg_create_forward_msg (TnyMsg *msg,
1063 const gchar *signature,
1064 ModestTnyMsgForwardType forward_type)
1067 TnyList *parts = NULL;
1068 GList *attachments_list = NULL;
1069 TnyMimePart *part_to_check = NULL;
1071 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1073 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
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);
1080 new_msg = create_reply_forward_mail (msg, NULL, from, signature, FALSE, forward_type,
1084 if (attachments_list) {
1085 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
1086 g_list_free (attachments_list);
1088 g_object_unref (G_OBJECT (parts));
1089 g_object_unref (part_to_check);
1097 count_addresses (const gchar* addresses)
1104 while (*addresses) {
1105 if (*addresses == ',' || *addresses == ';')
1114 remove_undisclosed_recipients (gchar **recipients)
1116 GSList *addresses, *node;
1120 g_return_if_fail (recipients);
1121 addresses = modest_text_utils_split_addresses_list (*recipients);
1124 result = g_string_new ("");
1125 for (node = addresses; node != NULL; node = g_slist_next (node)) {
1126 const gchar *address = (const gchar *) node->data;
1128 if (address && strstr (address, "undisclosed-recipients"))
1134 result = g_string_append (result, ", ");
1136 result = g_string_append (result, address);
1138 g_slist_foreach (addresses, (GFunc)g_free, NULL);
1139 g_slist_free (addresses);
1141 g_free (*recipients);
1142 *recipients = g_string_free (result, FALSE);
1146 /* get the new To:, based on the old header,
1147 * result is newly allocated or NULL in case of error
1150 get_new_to (TnyMsg *msg, TnyHeader *header, const gchar* from,
1151 ModestTnyMsgReplyMode reply_mode)
1153 const gchar *reply_header = "Reply-To:";
1154 const gchar *from_header = "From:";
1155 gchar* old_reply_to;
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
1165 gchar* list_help = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
1167 gboolean is_mailing_list = (list_help != NULL);
1171 /* reply to sender, use ReplyTo or From */
1172 old_reply_to = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
1174 old_from = tny_header_dup_from (header);
1176 if (!old_from && !old_reply_to) {
1177 g_debug ("%s: failed to get either Reply-To: or From: from header",
1182 /* Prevent DoS attacks caused by malformed emails */
1184 gchar *tmp = old_from;
1185 old_from = modest_text_utils_get_secure_header ((const gchar *) tmp, from_header);
1189 gchar *tmp = old_reply_to;
1190 old_reply_to = modest_text_utils_get_secure_header ((const gchar *) tmp, reply_header);
1194 /* for mailing lists, use both Reply-To and From if we did a
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);
1201 /* otherwise use either Reply-To: (preferred) or From: */
1202 new_to = g_strdup (old_reply_to ? old_reply_to : old_from);
1204 g_free (old_reply_to);
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);
1210 g_debug ("%s: no To: address found in source mail",
1213 /* append the old To: */
1214 gchar *tmp = g_strjoin (",", new_to, old_to, NULL);
1220 /* remove duplicate entries */
1221 gchar *tmp = modest_text_utils_remove_duplicate_addresses (new_to);
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);
1234 tmp = modest_text_utils_simplify_recipients (new_to);
1235 remove_undisclosed_recipients (&tmp);
1243 /* get the new Cc:, based on the old header,
1244 * result is newly allocated or NULL in case of error */
1246 get_new_cc (TnyHeader *header, const gchar* from, const gchar *new_to)
1248 gchar *old_cc, *result, *dup;
1250 old_cc = tny_header_dup_cc (header);
1254 /* remove me (the new From:) from the Cc: list */
1255 dup = modest_text_utils_remove_address (old_cc, from);
1258 gchar **to_parts, **current;
1260 to_parts = g_strsplit (new_to, ",", 0);
1261 for (current = to_parts; current && *current != '\0'; current++) {
1264 dup2 = modest_text_utils_remove_address (dup, g_strstrip (*current));
1268 g_strfreev (to_parts);
1271 result = modest_text_utils_remove_duplicate_addresses (dup);
1274 result = modest_text_utils_simplify_recipients (dup);
1275 remove_undisclosed_recipients (&result);
1282 modest_tny_msg_get_references (TnyMsg *msg, gchar **message_id, gchar **references, gchar **in_reply_to)
1285 TnyIterator *iterator;
1286 gchar *l_message_id;
1287 gchar *l_references;
1288 gchar *l_in_reply_to;
1290 g_return_if_fail (TNY_IS_MSG (msg));
1292 l_message_id = NULL;
1293 l_references = NULL;
1294 l_in_reply_to = NULL;
1296 headers = TNY_LIST (tny_simple_list_new ());
1297 tny_mime_part_get_header_pairs (TNY_MIME_PART (msg), headers);
1299 iterator = tny_list_create_iterator (headers);
1300 while (!tny_iterator_is_done (iterator)) {
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));
1317 g_object_unref (pair);
1318 tny_iterator_next (iterator);
1321 g_object_unref (iterator);
1322 g_object_unref (headers);
1325 *message_id = l_message_id;
1327 g_free (l_message_id);
1331 *in_reply_to = l_in_reply_to;
1333 g_free (l_in_reply_to);
1337 *references = l_references;
1339 g_free (l_references);
1344 remove_line_breaks (gchar *str)
1346 gchar *needle = g_strrstr (str, "\r\n");
1352 set_references (TnyMsg *reply_msg, TnyMsg *original_msg)
1354 gchar *orig_references, *orig_in_reply_to, *orig_message_id;
1355 gchar *references, *in_reply_to;
1357 modest_tny_msg_get_references (original_msg, &orig_message_id, &orig_references, &orig_in_reply_to);
1362 if (orig_message_id)
1363 in_reply_to = g_strdup (orig_message_id);
1365 if (orig_references) {
1366 if (orig_message_id)
1367 references = g_strconcat (orig_references, "\n ", orig_message_id, NULL);
1369 references = g_strdup (orig_references);
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);
1375 references = g_strdup (orig_in_reply_to);
1376 } else if (orig_message_id) {
1377 references = g_strdup (orig_message_id);
1380 g_free (orig_references);
1381 g_free (orig_in_reply_to);
1382 g_free (orig_message_id);
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);
1390 remove_line_breaks (references);
1391 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "References", references);
1392 g_free (references);
1397 modest_tny_msg_create_reply_msg (TnyMsg *msg,
1400 const gchar *signature,
1401 ModestTnyMsgReplyType reply_type,
1402 ModestTnyMsgReplyMode reply_mode)
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;
1411 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1413 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
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);
1419 new_msg = create_reply_forward_mail (msg, header, from, signature, TRUE, reply_type,
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);
1427 g_object_unref (parts);
1429 /* Fill the header */
1431 g_object_ref (header);
1433 header = tny_msg_get_header (msg);
1436 new_header = tny_msg_get_header(new_msg);
1437 new_to = get_new_to (msg, header, from, reply_mode);
1439 g_debug ("%s: failed to get new To:", __FUNCTION__);
1441 tny_header_set_to (new_header, new_to);
1444 if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1445 gchar *new_cc = get_new_cc (header, from, new_to);
1447 tny_header_set_cc (new_header, new_cc);
1456 g_object_unref (G_OBJECT (new_header));
1457 g_object_unref (G_OBJECT (header));
1458 g_object_unref (G_OBJECT (part_to_check));
1464 modest_tny_msg_create_reply_calendar_msg (TnyMsg *msg,
1467 const gchar *signature,
1470 TnyMsg *new_msg = NULL;
1471 TnyIterator *iterator;
1473 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
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);
1478 iterator = tny_list_create_iterator (headers);
1479 while (!tny_iterator_is_done (iterator)) {
1480 TnyPair *pair = TNY_PAIR (tny_iterator_get_current (iterator));
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);
1488 g_object_unref (iterator);
1495 is_ascii(const gchar *s)
1500 if (s[0] & 128 || s[0] < 32)
1508 get_content_type(const gchar *s)
1512 type = g_string_new("text/plain");
1514 if (g_utf8_validate(s, -1, NULL)) {
1515 g_string_append(type, "; charset=\"utf-8\"");
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\"");
1522 return g_string_free(type, FALSE);
1526 modest_tny_msg_estimate_size (const gchar *plain_body, const gchar *html_body,
1527 guint64 parts_count,
1532 /* estimation of headers size */
1535 /* We add a 20% of size due to the increase in 7bit encoding */
1537 result += strlen (plain_body) * 120 / 100;
1540 result += strlen (html_body) * 120 / 100;
1543 /* 256 bytes per additional part because of their headers */
1544 result += parts_count * 256;
1546 /* 150% of increase per encoding */
1547 result += parts_size * 3 / 2;
1553 modest_tny_msg_header_get_all_recipients_list (TnyHeader *header)
1555 GSList *recipients = NULL;
1556 gchar *from = NULL, *to = NULL, *cc = NULL, *bcc = NULL;
1557 gchar *after_remove, *joined;
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);
1567 joined = modest_text_utils_join_addresses (from, to, cc, bcc);
1568 after_remove = modest_text_utils_remove_duplicate_addresses (joined);
1571 recipients = modest_text_utils_split_addresses_list (after_remove);
1572 g_free (after_remove);
1587 modest_tny_msg_get_all_recipients_list (TnyMsg *msg)
1589 TnyHeader *header = NULL;
1590 GSList *recipients = NULL;
1595 header = tny_msg_get_header (msg);
1599 recipients = modest_tny_msg_header_get_all_recipients_list (header);
1600 g_object_unref (header);