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 modest_tny_msg_new (const gchar* mailto, const gchar* from, const gchar *cc,
66 const gchar *bcc, const gchar* subject,
67 const gchar *references, const gchar *in_reply_to,
69 GList *attachments, gint *attached, GError **err)
74 gint tmp_attached = 0;
77 new_msg = modest_formatter_create_message (NULL, TRUE, (attachments != NULL), FALSE);
78 header = tny_msg_get_header (new_msg);
80 if ((from != NULL) && (strlen(from) > 0)) {
81 tny_header_set_from (TNY_HEADER (header), from);
82 tny_header_set_replyto (TNY_HEADER (header), from);
84 if ((mailto != NULL) && (strlen(mailto) > 0)) {
85 gchar *removed_to = modest_text_utils_remove_duplicate_addresses (mailto);
86 tny_header_set_to (TNY_HEADER (header), removed_to);
89 if ((cc != NULL) && (strlen(cc) > 0))
90 tny_header_set_cc (TNY_HEADER (header), cc);
91 if ((bcc != NULL) && (strlen(bcc) > 0))
92 tny_header_set_bcc (TNY_HEADER (header), bcc);
94 if ((subject != NULL) && (strlen(subject) > 0))
95 tny_header_set_subject (TNY_HEADER (header), subject);
97 content_type = get_content_type(body);
99 /* set modest as the X-Mailer
100 * we could this in the platform factory, but then the header
101 * would show up before all the others.
103 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "X-Mailer", "Modest "
107 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "References", references);
110 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "In-Reply-To", in_reply_to);
112 /* Add the body of the new mail */
113 /* This is needed even if body is NULL or empty. */
114 add_body_part (new_msg, body, content_type);
115 g_free (content_type);
117 /* Add attachments */
119 tmp_attached = add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, err);
121 *attached = tmp_attached;
123 g_object_unref(header);
129 modest_tny_msg_new_html_plain (const gchar* mailto, const gchar* from, const gchar *cc,
130 const gchar *bcc, const gchar* subject,
131 const gchar *references, const gchar *in_reply_to,
132 const gchar *html_body, const gchar *plain_body,
133 GList *attachments, GList *images, gint *attached, GError **err)
141 new_msg = modest_formatter_create_message (NULL, FALSE, (attachments != NULL), (images != NULL));
142 header = tny_msg_get_header (new_msg);
144 if ((from != NULL) && (strlen(from) > 0)) {
145 tny_header_set_from (TNY_HEADER (header), from);
146 tny_header_set_replyto (TNY_HEADER (header), from);
148 if ((mailto != NULL) && (strlen(mailto) > 0))
149 tny_header_set_to (TNY_HEADER (header), mailto);
150 if ((cc != NULL) && (strlen(cc) > 0))
151 tny_header_set_cc (TNY_HEADER (header), cc);
152 if ((bcc != NULL) && (strlen(bcc) > 0))
153 tny_header_set_bcc (TNY_HEADER (header), bcc);
155 if ((subject != NULL) && (strlen(subject) > 0))
156 tny_header_set_subject (TNY_HEADER (header), subject);
158 content_type = get_content_type(plain_body);
160 /* set modest as the X-Mailer
161 * we could this in the platform factory, but then the header
162 * would show up before all the others.
164 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "X-Mailer", "Modest "
168 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "References", references);
171 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "In-Reply-To", in_reply_to);
173 /* Add the body of the new mail */
174 add_body_part (new_msg, plain_body, content_type);
175 add_html_body_part (new_msg, html_body);
176 g_free (content_type);
178 /* Add attachments */
179 tmp_attached = add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, err);
181 *attached = tmp_attached;
182 add_images (new_msg, images, err);
184 g_object_unref(header);
190 /* FIXME: this func copy from modest-mail-operation: refactor */
192 add_body_part (TnyMsg *msg,
194 const gchar *content_type)
196 TnyMimePart *text_body_part = NULL;
197 TnyStream *text_body_stream;
199 /* Create the stream */
200 text_body_stream = TNY_STREAM (tny_camel_mem_stream_new_with_buffer
201 (body, (body ? strlen(body) : 0)));
203 text_body_part = modest_formatter_create_body_part (NULL, msg);
205 /* Construct MIME part */
206 tny_stream_reset (text_body_stream);
207 tny_mime_part_construct (text_body_part,
209 content_type, "7bit");
210 tny_stream_reset (text_body_stream);
212 g_object_unref (G_OBJECT(text_body_part));
215 g_object_unref (text_body_stream);
217 return text_body_part;
221 add_html_body_part (TnyMsg *msg,
224 TnyMimePart *html_body_part = NULL;
225 TnyStream *html_body_stream;
227 /* Create the stream */
228 html_body_stream = TNY_STREAM (tny_camel_mem_stream_new_with_buffer
229 (body, (body) ? strlen(body) : 0));
231 /* Create body part if needed */
232 html_body_part = modest_formatter_create_body_part (NULL, msg);
234 /* Construct MIME part */
235 tny_stream_reset (html_body_stream);
236 tny_mime_part_construct (html_body_part,
238 "text/html; charset=utf-8",
239 "7bit"); /* Sometimes it might be needed
240 to make this one a 8bit! */
241 tny_stream_reset (html_body_stream);
243 g_object_unref (G_OBJECT(html_body_part));
246 g_object_unref (html_body_stream);
248 return html_body_part;
252 copy_mime_part (TnyMimePart *part, GError **err)
254 TnyMimePart *result = NULL;
255 const gchar *attachment_content_type;
256 const gchar *attachment_filename;
257 const gchar *attachment_cid;
259 TnyIterator *iterator;
260 TnyStream *attachment_stream;
264 if (TNY_IS_MSG (part)) {
269 result = tny_platform_factory_new_mime_part (
270 modest_runtime_get_platform_factory());
272 attachment_content_type = tny_mime_part_get_content_type (part);
274 /* get mime part headers */
275 attachment_filename = tny_mime_part_get_filename (part);
276 attachment_cid = tny_mime_part_get_content_id (part);
278 /* fill the stream */
279 attachment_stream = tny_mime_part_get_decoded_stream (part);
280 enc = tny_mime_part_get_transfer_encoding (part);
281 if (attachment_stream == NULL) {
282 if (err != NULL && *err == NULL)
283 g_set_error (err, MODEST_MAIL_OPERATION_ERROR, MODEST_MAIL_OPERATION_ERROR_FILE_IO, _("TODO: couldn't retrieve attachment"));
284 g_object_unref (result);
287 ret = tny_stream_reset (attachment_stream);
288 ret = tny_mime_part_construct (result,
290 attachment_content_type,
292 ret = tny_stream_reset (attachment_stream);
295 /* set other mime part fields */
296 tny_mime_part_set_filename (result, attachment_filename);
297 tny_mime_part_set_content_id (result, attachment_cid);
300 parts = tny_simple_list_new ();
301 tny_mime_part_get_parts (part, parts);
302 iterator = tny_list_create_iterator (parts);
303 while (!tny_iterator_is_done (iterator)) {
304 TnyMimePart *subpart = TNY_MIME_PART (tny_iterator_get_current (iterator));
306 const gchar *subpart_cid;
307 TnyMimePart *subpart_copy = copy_mime_part (subpart, err);
308 if (subpart_copy != NULL) {
309 subpart_cid = tny_mime_part_get_content_id (subpart);
310 tny_mime_part_add_part (result, subpart_copy);
312 tny_mime_part_set_content_id (result, subpart_cid);
313 g_object_unref (subpart_copy);
315 g_object_unref (subpart);
318 tny_iterator_next (iterator);
320 g_object_unref (iterator);
321 g_object_unref (parts);
322 g_object_unref (attachment_stream);
328 add_attachments (TnyMimePart *part, GList *attachments_list, gboolean add_inline, GError **err)
331 TnyMimePart *attachment_part, *old_attachment;
335 for (pos = (GList *)attachments_list; pos; pos = pos->next) {
337 old_attachment = pos->data;
338 if (!tny_mime_part_is_purged (old_attachment)) {
340 old_cid = g_strdup (tny_mime_part_get_content_id (old_attachment));
341 attachment_part = copy_mime_part (old_attachment, err);
342 if (attachment_part != NULL) {
344 tny_mime_part_set_header_pair (attachment_part, "Content-Disposition",
347 const gchar *filename;
349 filename = tny_mime_part_get_filename (old_attachment);
351 /* If the mime part has a filename do not set it again
352 because Camel won't replace the old one. Instead it
353 will append the filename to the old one and that will
354 mislead email clients */
355 if (!tny_mime_part_get_filename (attachment_part))
356 tny_mime_part_set_filename (attachment_part, filename);
358 tny_mime_part_set_header_pair (attachment_part, "Content-Disposition",
362 if (!TNY_IS_MSG (old_attachment)) {
363 tny_mime_part_set_transfer_encoding (TNY_MIME_PART (attachment_part), "base64");
365 ret = tny_mime_part_add_part (TNY_MIME_PART (part), attachment_part);
368 tny_mime_part_set_content_id (attachment_part, old_cid);
369 g_object_unref (attachment_part);
378 add_images (TnyMsg *msg, GList *images_list, GError **err)
380 TnyMimePart *related_part = NULL;
381 const gchar *content_type;
383 content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
385 if ((content_type != NULL) && !strcasecmp (content_type, "multipart/related")) {
386 related_part = g_object_ref (msg);
387 } else if ((content_type != NULL) && !strcasecmp (content_type, "multipart/mixed")) {
388 TnyList *parts = TNY_LIST (tny_simple_list_new ());
389 TnyIterator *iter = NULL;
390 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
391 iter = tny_list_create_iterator (parts);
393 while (!tny_iterator_is_done (iter)) {
394 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
395 if (part && !g_ascii_strcasecmp (tny_mime_part_get_content_type (part), "multipart/related")) {
400 g_object_unref (part);
401 tny_iterator_next (iter);
403 g_object_unref (iter);
404 g_object_unref (parts);
407 if (related_part != NULL) {
408 /* TODO: attach images in their proper place */
409 add_attachments (related_part, images_list, TRUE, err);
410 g_object_unref (related_part);
416 modest_tny_msg_get_body (TnyMsg *msg, gboolean want_html, gboolean *is_html)
421 GtkTextIter start, end;
423 gboolean result_was_html = TRUE;
425 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
427 body = modest_tny_msg_find_body_part(msg, want_html);
431 buf = gtk_text_buffer_new (NULL);
432 stream = TNY_STREAM (tny_gtk_text_buffer_stream_new (buf));
433 tny_stream_reset (stream);
434 tny_mime_part_decode_to_stream (body, stream, NULL);
435 tny_stream_reset (stream);
437 gtk_text_buffer_get_bounds (buf, &start, &end);
438 to_quote = gtk_text_buffer_get_text (buf, &start, &end, FALSE);
439 if (tny_mime_part_content_type_is (body, "text/plain")) {
440 gchar *to_quote_converted = modest_text_utils_convert_to_html (to_quote);
442 to_quote = to_quote_converted;
443 result_was_html = FALSE;
446 g_object_unref (buf);
447 g_object_unref (G_OBJECT(stream));
448 g_object_unref (G_OBJECT(body));
451 *is_html = result_was_html;
458 modest_tny_msg_find_body_part_in_alternative (TnyMimePart *msg, gboolean want_html)
462 TnyMimePart *part = NULL, *related = NULL;
463 TnyMimePart *first_part = NULL;
464 const gchar *desired_mime_type = want_html ? "text/html" : "text/plain";
466 parts = TNY_LIST (tny_simple_list_new());
467 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
469 for (iter = tny_list_create_iterator(parts);
470 !tny_iterator_is_done (iter);
471 tny_iterator_next (iter)) {
475 part = TNY_MIME_PART (tny_iterator_get_current (iter));
477 if (first_part == NULL) {
483 content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
484 is_body = g_str_has_prefix (content_type, desired_mime_type);
488 /* Makes no sense to look for related MIME parts if we
489 only want the plain text parts */
490 if (want_html && g_str_has_prefix (content_type, "multipart/related")) {
491 /* In an alternative the last part is supposed
494 g_object_unref (related);
495 related = g_object_ref (part);
498 g_object_unref (part);
502 g_object_unref (iter);
503 g_object_unref (parts);
509 g_object_unref (first_part);
510 retval = modest_tny_msg_find_body_part_from_mime_part (related, want_html);
511 g_object_unref (related);
518 g_object_unref (first_part);
520 g_object_unref (related);
526 modest_tny_msg_find_body_part_from_mime_part (TnyMimePart *msg, gboolean want_html)
528 TnyMimePart *part = NULL;
529 TnyList *parts = NULL;
530 TnyIterator *iter = NULL;
531 gchar *header_content_type;
532 gchar *header_content_type_lower = NULL;
537 /* If it's an application multipart, then we don't get into as we don't
538 * support them (for example application/sml or wap messages */
539 header_content_type = modest_tny_mime_part_get_header_value (msg, "Content-Type");
540 if (header_content_type) {
541 header_content_type = g_strstrip (header_content_type);
542 header_content_type_lower = g_ascii_strdown (header_content_type, -1);
544 if (header_content_type_lower &&
545 g_str_has_prefix (header_content_type_lower, "multipart/") &&
546 !g_str_has_prefix (header_content_type_lower, "multipart/signed") &&
547 strstr (header_content_type_lower, "application/")) {
548 g_free (header_content_type_lower);
549 g_free (header_content_type);
552 if (header_content_type_lower &&
553 g_str_has_prefix (header_content_type_lower, "multipart/alternative")) {
554 g_free (header_content_type_lower);
555 g_free (header_content_type);
556 return modest_tny_msg_find_body_part_in_alternative (msg, want_html);
558 g_free (header_content_type_lower);
559 g_free (header_content_type);
561 parts = TNY_LIST (tny_simple_list_new());
562 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
564 iter = tny_list_create_iterator(parts);
566 /* no parts? assume it's single-part message */
567 if (tny_iterator_is_done(iter)) {
569 gboolean is_text_part;
570 g_object_unref (G_OBJECT(iter));
571 content_type = modest_tny_mime_part_get_content_type (msg);
572 if (content_type == NULL)
575 g_str_has_prefix (content_type, "text/");
576 g_free (content_type);
577 /* if this part cannot be a supported body return NULL */
581 return TNY_MIME_PART (g_object_ref(G_OBJECT(msg)));
585 gchar *tmp, *content_type = NULL;
586 gboolean has_content_disp_name = FALSE;
588 part = TNY_MIME_PART(tny_iterator_get_current (iter));
591 g_warning ("%s: not a valid mime part", __FUNCTION__);
592 tny_iterator_next (iter);
596 /* it's a message --> ignore */
597 if (part && TNY_IS_MSG (part)) {
598 g_object_unref (part);
600 tny_iterator_next (iter);
604 /* we need to strdown the content type, because
605 * tny_mime_part_has_content_type does not do it...
607 content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
609 /* mime-parts with a content-disposition header (either 'inline' or 'attachment')
610 * and a 'name=' thingy cannot be body parts
613 tmp = modest_tny_mime_part_get_header_value (part, "Content-Disposition");
615 gchar *content_disp = g_ascii_strdown(tmp, -1);
617 has_content_disp_name = g_strstr_len (content_disp, strlen(content_disp), "name=") != NULL;
618 g_free (content_disp);
621 if (g_str_has_prefix (content_type, "text/") &&
622 !has_content_disp_name &&
623 !modest_tny_mime_part_is_attachment_for_modest (part)) {
624 /* we found the body. Doesn't have to be the desired mime part, first
625 text/ part in a mixed is the body */
626 g_free (content_type);
629 } else if (g_str_has_prefix(content_type, "multipart/alternative")) {
631 /* multipart? recurse! */
632 g_object_unref (part);
633 g_free (content_type);
634 part = modest_tny_msg_find_body_part_in_alternative (part, want_html);
638 } else if (g_str_has_prefix(content_type, "multipart")) {
640 /* multipart? recurse! */
641 g_object_unref (part);
642 g_free (content_type);
643 part = modest_tny_msg_find_body_part_from_mime_part (part, want_html);
647 g_free (content_type);
650 g_object_unref (G_OBJECT(part));
654 tny_iterator_next (iter);
656 } while (!tny_iterator_is_done(iter));
659 g_object_unref (G_OBJECT(iter));
660 g_object_unref (G_OBJECT(parts));
662 return part; /* this maybe NULL, this is not an error; some message just don't have a body
667 modest_tny_msg_find_calendar_from_mime_part (TnyMimePart *msg)
669 TnyMimePart *part = NULL;
670 TnyList *parts = NULL;
671 TnyIterator *iter = NULL;
672 gchar *header_content_type;
673 gchar *header_content_type_lower = NULL;
678 /* If it's an application multipart, then we don't get into as we don't
679 * support them (for example application/sml or wap messages */
680 header_content_type = modest_tny_mime_part_get_header_value (msg, "Content-Type");
681 if (header_content_type) {
682 header_content_type = g_strstrip (header_content_type);
683 header_content_type_lower = g_ascii_strdown (header_content_type, -1);
685 if (header_content_type_lower &&
686 g_str_has_prefix (header_content_type_lower, "multipart/") &&
687 !g_str_has_prefix (header_content_type_lower, "multipart/signed") &&
688 strstr (header_content_type_lower, "application/")) {
689 g_free (header_content_type_lower);
690 g_free (header_content_type);
693 g_free (header_content_type_lower);
694 g_free (header_content_type);
696 parts = TNY_LIST (tny_simple_list_new());
697 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
699 iter = tny_list_create_iterator(parts);
701 /* no parts? assume it's single-part message */
702 if (tny_iterator_is_done(iter)) {
704 gboolean is_calendar_part;
705 g_object_unref (G_OBJECT(iter));
706 content_type = modest_tny_mime_part_get_content_type (msg);
707 if (content_type == NULL)
710 g_str_has_prefix (content_type, "text/calendar");
711 g_free (content_type);
712 /* if this part cannot be a supported body return NULL */
713 if (!is_calendar_part) {
716 return TNY_MIME_PART (g_object_ref(G_OBJECT(msg)));
720 gchar *tmp, *content_type = NULL;
721 gboolean has_content_disp_name = FALSE;
723 part = TNY_MIME_PART(tny_iterator_get_current (iter));
726 g_warning ("%s: not a valid mime part", __FUNCTION__);
727 tny_iterator_next (iter);
731 /* it's a message --> ignore */
732 if (part && TNY_IS_MSG (part)) {
733 g_object_unref (part);
735 tny_iterator_next (iter);
739 /* we need to strdown the content type, because
740 * tny_mime_part_has_content_type does not do it...
742 content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
744 /* mime-parts with a content-disposition header (either 'inline' or 'attachment')
745 * and a 'name=' thingy cannot be body parts
748 tmp = modest_tny_mime_part_get_header_value (part, "Content-Disposition");
750 gchar *content_disp = g_ascii_strdown(tmp, -1);
752 has_content_disp_name = g_strstr_len (content_disp, strlen(content_disp), "name=") != NULL;
753 g_free (content_disp);
756 if (g_str_has_prefix (content_type, "text/calendar") &&
757 !has_content_disp_name &&
758 !modest_tny_mime_part_is_attachment_for_modest (part)) {
759 /* we found the body. Doesn't have to be the desired mime part, first
760 text/ part in a mixed is the body */
761 g_free (content_type);
764 } else if (g_str_has_prefix(content_type, "multipart")) {
766 /* multipart? recurse! */
767 g_object_unref (part);
768 g_free (content_type);
769 part = modest_tny_msg_find_calendar_from_mime_part (part);
773 g_free (content_type);
776 g_object_unref (G_OBJECT(part));
780 tny_iterator_next (iter);
782 } while (!tny_iterator_is_done(iter));
785 g_object_unref (G_OBJECT(iter));
786 g_object_unref (G_OBJECT(parts));
788 return part; /* this maybe NULL, this is not an error; some message just don't have a body
794 modest_tny_msg_find_body_part (TnyMsg *msg, gboolean want_html)
796 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
798 return modest_tny_msg_find_body_part_from_mime_part (TNY_MIME_PART(msg),
803 modest_tny_msg_find_calendar (TnyMsg *msg)
805 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
807 return modest_tny_msg_find_calendar_from_mime_part (TNY_MIME_PART(msg));
811 #define MODEST_TNY_MSG_PARENT_UID "parent-uid"
814 create_reply_forward_mail (TnyMsg *msg, TnyHeader *header, const gchar *from,
815 const gchar *signature, gboolean is_reply,
816 guint type /*ignored*/, GList *attachments)
819 TnyHeader *new_header;
822 TnyMimePart *body = NULL;
823 TnyMimePart *html_body = NULL;
824 ModestFormatter *formatter;
825 gboolean no_text_part;
827 gboolean forward_as_attach = FALSE;
830 g_object_ref (header);
832 header = tny_msg_get_header (msg);
834 /* Get body from original msg. Always look for the text/plain
835 part of the message to create the reply/forwarded mail */
837 body = modest_tny_msg_find_body_part (msg, FALSE);
838 html_body = modest_tny_msg_find_body_part (msg, TRUE);
841 if (modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT,
843 formatter = modest_formatter_new ("text/html", signature);
845 formatter = modest_formatter_new ("text/plain", signature);
848 /* if we don't have a text-part */
849 no_text_part = (!body) || (strcmp (tny_mime_part_get_content_type (body), "text/html")==0);
851 /* when we're reply, include the text part if we have it, or nothing otherwise. */
853 new_msg = modest_formatter_quote (formatter, body, header,
856 if (no_text_part || (html_body && (strcmp (tny_mime_part_get_content_type (html_body), "text/html")==0))) {
857 forward_as_attach = TRUE;
858 new_msg = modest_formatter_attach (formatter, msg, header);
860 forward_as_attach = FALSE;
861 new_msg = modest_formatter_inline (formatter, body, header,
866 g_object_unref (G_OBJECT(formatter));
868 g_object_unref (G_OBJECT(body));
870 g_object_unref (G_OBJECT(html_body));
872 /* Fill the header */
873 new_header = tny_msg_get_header (new_msg);
874 tny_header_set_from (new_header, from);
875 tny_header_set_replyto (new_header, from);
877 /* Change the subject */
878 old_subject = tny_header_dup_subject (header);
880 (gchar *) modest_text_utils_derived_subject (old_subject, is_reply);
881 g_free (old_subject);
882 tny_header_set_subject (new_header, (const gchar *) new_subject);
883 g_free (new_subject);
885 /* get the parent uid, and set it as a gobject property on the new msg */
886 parent_uid = modest_tny_folder_get_header_unique_id (header);
887 g_object_set_data_full (G_OBJECT(new_msg), MODEST_TNY_MSG_PARENT_UID,
890 /* set modest as the X-Mailer
891 * we could this in the platform factory, but then the header
892 * would show up before all the others.
894 tny_mime_part_set_header_pair (TNY_MIME_PART (msg), "X-Mailer", "Modest "
898 g_object_unref (G_OBJECT (new_header));
899 g_object_unref (G_OBJECT (header));
900 /* ugly to unref it here instead of in the calling func */
902 if (!is_reply & !forward_as_attach) {
903 add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, NULL);
910 modest_tny_msg_get_parent_uid (TnyMsg *msg)
912 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
914 return g_object_get_data (G_OBJECT(msg), MODEST_TNY_MSG_PARENT_UID);
917 static gchar *get_signed_protocol (TnyMimePart *part)
919 TnyList *header_pairs;
920 TnyIterator *iterator;
921 gchar *result = NULL;
923 header_pairs = TNY_LIST (tny_simple_list_new ());
924 tny_mime_part_get_header_pairs (part, header_pairs);
925 iterator = tny_list_create_iterator (header_pairs);
927 while (!result && !tny_iterator_is_done (iterator)) {
931 pair = TNY_PAIR (tny_iterator_get_current (iterator));
932 name = tny_pair_get_name (pair);
933 if (name && !g_ascii_strcasecmp (name, "Content-Type")) {
935 s = tny_pair_get_value (pair);
937 s = strstr (s, "protocol=");
943 t = strstr (s, "\"");
947 result = g_strndup (s, t - s);
952 g_object_unref (pair);
953 tny_iterator_next (iterator);
956 g_object_unref (iterator);
957 g_object_unref (header_pairs);
963 modest_tny_msg_get_attachments_parent (TnyMsg *msg)
966 const gchar *content_type;
970 content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
971 if (content_type && !strcmp (content_type, "multipart/signed")) {
972 TnyList *msg_children;
973 TnyIterator *iterator;
974 gchar *signed_protocol;
976 msg_children = TNY_LIST (tny_simple_list_new ());
977 tny_mime_part_get_parts (TNY_MIME_PART (msg), msg_children);
979 iterator = tny_list_create_iterator (msg_children);
980 signed_protocol = get_signed_protocol (TNY_MIME_PART (msg));
982 while (!result && !tny_iterator_is_done (iterator)) {
985 part = TNY_MIME_PART (tny_iterator_get_current (iterator));
986 if (signed_protocol) {
987 const gchar *part_content_type;
989 part_content_type = tny_mime_part_get_content_type (part);
990 if (part_content_type && g_ascii_strcasecmp (part_content_type, signed_protocol)) {
991 result = g_object_ref (part);
994 result = g_object_ref (part);
997 g_object_unref (part);
998 tny_iterator_next (iterator);
1001 g_object_unref (iterator);
1002 g_free (signed_protocol);
1003 g_object_unref (msg_children);
1005 if (result == NULL) {
1006 result = g_object_ref (msg);
1015 add_if_attachment (gpointer data, gpointer user_data)
1018 GList **attachments_list;
1020 part = TNY_MIME_PART (data);
1021 attachments_list = ((GList **) user_data);
1023 if (!tny_mime_part_is_purged (part) && ((tny_mime_part_is_attachment (part))||(TNY_IS_MSG (part)))) {
1024 *attachments_list = g_list_prepend (*attachments_list, part);
1025 g_object_ref (part);
1030 modest_tny_msg_create_forward_msg (TnyMsg *msg,
1032 const gchar *signature,
1033 ModestTnyMsgForwardType forward_type)
1036 TnyList *parts = NULL;
1037 GList *attachments_list = NULL;
1038 TnyMimePart *part_to_check = NULL;
1040 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1042 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
1044 /* Add attachments */
1045 parts = TNY_LIST (tny_simple_list_new());
1046 tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
1047 tny_list_foreach (parts, add_if_attachment, &attachments_list);
1049 new_msg = create_reply_forward_mail (msg, NULL, from, signature, FALSE, forward_type,
1053 if (attachments_list) {
1054 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
1055 g_list_free (attachments_list);
1057 g_object_unref (G_OBJECT (parts));
1058 g_object_unref (part_to_check);
1066 count_addresses (const gchar* addresses)
1073 while (*addresses) {
1074 if (*addresses == ',' || *addresses == ';')
1083 remove_undisclosed_recipients (gchar **recipients)
1085 GSList *addresses, *node;
1089 g_return_if_fail (recipients);
1090 addresses = modest_text_utils_split_addresses_list (*recipients);
1093 result = g_string_new ("");
1094 for (node = addresses; node != NULL; node = g_slist_next (node)) {
1095 const gchar *address = (const gchar *) node->data;
1097 if (address && strstr (address, "undisclosed-recipients"))
1103 result = g_string_append (result, ", ");
1105 result = g_string_append (result, address);
1107 g_slist_foreach (addresses, (GFunc)g_free, NULL);
1108 g_slist_free (addresses);
1110 g_free (*recipients);
1111 *recipients = g_string_free (result, FALSE);
1115 /* get the new To:, based on the old header,
1116 * result is newly allocated or NULL in case of error
1119 get_new_to (TnyMsg *msg, TnyHeader *header, const gchar* from,
1120 ModestTnyMsgReplyMode reply_mode)
1122 const gchar *reply_header = "Reply-To:";
1123 const gchar *from_header = "From:";
1124 gchar* old_reply_to;
1129 /* according to RFC2369 (http://www.faqs.org/rfcs/rfc2369.html), we
1130 * can identify Mailing-List posts by the List-Help header.
1131 * for mailing lists, both the Reply-To: and From: should be included
1132 * in the new To:; for now, we're ignoring List-Post
1134 gchar* list_help = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
1136 gboolean is_mailing_list = (list_help != NULL);
1140 /* reply to sender, use ReplyTo or From */
1141 old_reply_to = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
1143 old_from = tny_header_dup_from (header);
1145 if (!old_from && !old_reply_to) {
1146 g_debug ("%s: failed to get either Reply-To: or From: from header",
1151 /* Prevent DoS attacks caused by malformed emails */
1153 gchar *tmp = old_from;
1154 old_from = modest_text_utils_get_secure_header ((const gchar *) tmp, from_header);
1158 gchar *tmp = old_reply_to;
1159 old_reply_to = modest_text_utils_get_secure_header ((const gchar *) tmp, reply_header);
1163 /* for mailing lists, use both Reply-To and From if we did a
1166 if (is_mailing_list && reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL &&
1167 old_reply_to && old_from && strcmp (old_from, old_reply_to) != 0)
1168 new_to = g_strjoin (",", old_reply_to, old_from, NULL);
1170 /* otherwise use either Reply-To: (preferred) or From: */
1171 new_to = g_strdup (old_reply_to ? old_reply_to : old_from);
1173 g_free (old_reply_to);
1175 /* in case of ReplyAll, we need to add the Recipients in the old To: */
1176 if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1177 gchar *old_to = tny_header_dup_to (header);
1179 g_debug ("%s: no To: address found in source mail",
1182 /* append the old To: */
1183 gchar *tmp = g_strjoin (",", new_to, old_to, NULL);
1189 /* remove duplicate entries */
1190 gchar *tmp = modest_text_utils_remove_duplicate_addresses (new_to);
1194 /* now, strip me (the new From:) from the new_to, but only if
1195 * there are >1 addresses there */
1196 if (count_addresses (new_to) > 1) {
1197 gchar *tmp = modest_text_utils_remove_address (new_to, from);
1203 tmp = modest_text_utils_simplify_recipients (new_to);
1204 remove_undisclosed_recipients (&tmp);
1212 /* get the new Cc:, based on the old header,
1213 * result is newly allocated or NULL in case of error */
1215 get_new_cc (TnyHeader *header, const gchar* from, const gchar *new_to)
1217 gchar *old_cc, *result, *dup;
1219 old_cc = tny_header_dup_cc (header);
1223 /* remove me (the new From:) from the Cc: list */
1224 dup = modest_text_utils_remove_address (old_cc, from);
1227 gchar **to_parts, **current;
1229 to_parts = g_strsplit (new_to, ",", 0);
1230 for (current = to_parts; current && *current != '\0'; current++) {
1233 dup2 = modest_text_utils_remove_address (dup, g_strstrip (*current));
1237 g_strfreev (to_parts);
1240 result = modest_text_utils_remove_duplicate_addresses (dup);
1243 result = modest_text_utils_simplify_recipients (dup);
1244 remove_undisclosed_recipients (&result);
1251 modest_tny_msg_get_references (TnyMsg *msg, gchar **message_id, gchar **references, gchar **in_reply_to)
1254 TnyIterator *iterator;
1255 gchar *l_message_id;
1256 gchar *l_references;
1257 gchar *l_in_reply_to;
1259 g_return_if_fail (TNY_IS_MSG (msg));
1261 l_message_id = NULL;
1262 l_references = NULL;
1263 l_in_reply_to = NULL;
1265 headers = TNY_LIST (tny_simple_list_new ());
1266 tny_mime_part_get_header_pairs (TNY_MIME_PART (msg), headers);
1268 iterator = tny_list_create_iterator (headers);
1269 while (!tny_iterator_is_done (iterator)) {
1273 pair = TNY_PAIR (tny_iterator_get_current (iterator));
1274 name = tny_pair_get_name (pair);
1275 if (!g_ascii_strcasecmp (name, "References")) {
1276 if (l_references) g_free (l_references);
1277 l_references = g_strdup (tny_pair_get_value (pair));
1278 } else if (!g_ascii_strcasecmp (name, "In-Reply-To")) {
1279 if (l_in_reply_to) g_free (l_in_reply_to);
1280 l_in_reply_to = g_strdup (tny_pair_get_value (pair));
1281 } else if (!g_ascii_strcasecmp (name, "Message-ID")) {
1282 if (l_message_id) g_free (l_message_id);
1283 l_message_id = g_strdup (tny_pair_get_value (pair));
1286 g_object_unref (pair);
1287 tny_iterator_next (iterator);
1290 g_object_unref (iterator);
1291 g_object_unref (headers);
1294 *message_id = l_message_id;
1296 g_free (l_message_id);
1300 *in_reply_to = l_in_reply_to;
1302 g_free (l_in_reply_to);
1306 *references = l_references;
1308 g_free (l_references);
1313 remove_line_breaks (gchar *str)
1315 gchar *needle = g_strrstr (str, "\r\n");
1321 set_references (TnyMsg *reply_msg, TnyMsg *original_msg)
1323 gchar *orig_references, *orig_in_reply_to, *orig_message_id;
1324 gchar *references, *in_reply_to;
1326 modest_tny_msg_get_references (original_msg, &orig_message_id, &orig_references, &orig_in_reply_to);
1331 if (orig_message_id)
1332 in_reply_to = g_strdup (orig_message_id);
1334 if (orig_references) {
1335 if (orig_message_id)
1336 references = g_strconcat (orig_references, "\n ", orig_message_id, NULL);
1338 references = g_strdup (orig_references);
1340 } else if (orig_in_reply_to) {
1341 if (orig_message_id)
1342 references = g_strconcat (orig_in_reply_to, "\n ", orig_message_id, NULL);
1344 references = g_strdup (orig_in_reply_to);
1345 } else if (orig_message_id) {
1346 references = g_strdup (orig_message_id);
1349 g_free (orig_references);
1350 g_free (orig_in_reply_to);
1351 g_free (orig_message_id);
1354 remove_line_breaks (in_reply_to);
1355 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "In-Reply-To", in_reply_to);
1356 g_free (in_reply_to);
1359 remove_line_breaks (references);
1360 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "References", references);
1361 g_free (references);
1366 modest_tny_msg_create_reply_msg (TnyMsg *msg,
1369 const gchar *signature,
1370 ModestTnyMsgReplyType reply_type,
1371 ModestTnyMsgReplyMode reply_mode)
1373 TnyMsg *new_msg = NULL;
1374 TnyHeader *new_header;
1375 gchar *new_to = NULL;
1376 TnyList *parts = NULL;
1377 GList *attachments_list = NULL;
1378 TnyMimePart *part_to_check = NULL;
1380 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1382 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
1384 parts = TNY_LIST (tny_simple_list_new());
1385 tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
1386 tny_list_foreach (parts, add_if_attachment, &attachments_list);
1388 new_msg = create_reply_forward_mail (msg, header, from, signature, TRUE, reply_type,
1391 set_references (new_msg, msg);
1392 if (attachments_list != NULL) {
1393 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
1394 g_list_free (attachments_list);
1396 g_object_unref (parts);
1398 /* Fill the header */
1400 g_object_ref (header);
1402 header = tny_msg_get_header (msg);
1405 new_header = tny_msg_get_header(new_msg);
1406 new_to = get_new_to (msg, header, from, reply_mode);
1408 g_debug ("%s: failed to get new To:", __FUNCTION__);
1410 tny_header_set_to (new_header, new_to);
1413 if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1414 gchar *new_cc = get_new_cc (header, from, new_to);
1416 tny_header_set_cc (new_header, new_cc);
1425 g_object_unref (G_OBJECT (new_header));
1426 g_object_unref (G_OBJECT (header));
1427 g_object_unref (G_OBJECT (part_to_check));
1433 modest_tny_msg_create_reply_calendar_msg (TnyMsg *msg,
1436 const gchar *signature,
1439 TnyMsg *new_msg = NULL;
1440 TnyIterator *iterator;
1442 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1444 new_msg = modest_tny_msg_create_reply_msg (msg, header, from, signature,
1445 MODEST_TNY_MSG_REPLY_TYPE_QUOTE, MODEST_TNY_MSG_REPLY_MODE_SENDER);
1447 iterator = tny_list_create_iterator (headers);
1448 while (!tny_iterator_is_done (iterator)) {
1449 TnyPair *pair = TNY_PAIR (tny_iterator_get_current (iterator));
1451 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg),
1452 tny_pair_get_name (pair),
1453 tny_pair_get_value (pair));
1454 g_object_unref (pair);
1455 tny_iterator_next (iterator);
1457 g_object_unref (iterator);
1464 is_ascii(const gchar *s)
1469 if (s[0] & 128 || s[0] < 32)
1477 get_content_type(const gchar *s)
1481 type = g_string_new("text/plain");
1483 if (g_utf8_validate(s, -1, NULL)) {
1484 g_string_append(type, "; charset=\"utf-8\"");
1486 /* it should be impossible to reach this, but better safe than sorry */
1487 g_debug("invalid utf8 in message");
1488 g_string_append(type, "; charset=\"latin1\"");
1491 return g_string_free(type, FALSE);
1495 modest_tny_msg_estimate_size (const gchar *plain_body, const gchar *html_body,
1496 guint64 parts_count,
1501 /* estimation of headers size */
1504 /* We add a 20% of size due to the increase in 7bit encoding */
1506 result += strlen (plain_body) * 120 / 100;
1509 result += strlen (html_body) * 120 / 100;
1512 /* 256 bytes per additional part because of their headers */
1513 result += parts_count * 256;
1515 /* 150% of increase per encoding */
1516 result += parts_size * 3 / 2;
1522 modest_tny_msg_header_get_all_recipients_list (TnyHeader *header)
1524 GSList *recipients = NULL;
1525 gchar *from = NULL, *to = NULL, *cc = NULL, *bcc = NULL;
1526 gchar *after_remove, *joined;
1531 from = tny_header_dup_from (header);
1532 to = tny_header_dup_to (header);
1533 cc = tny_header_dup_cc (header);
1534 bcc = tny_header_dup_bcc (header);
1536 joined = modest_text_utils_join_addresses (from, to, cc, bcc);
1537 after_remove = modest_text_utils_remove_duplicate_addresses (joined);
1540 recipients = modest_text_utils_split_addresses_list (after_remove);
1541 g_free (after_remove);
1556 modest_tny_msg_get_all_recipients_list (TnyMsg *msg)
1558 TnyHeader *header = NULL;
1559 GSList *recipients = NULL;
1564 header = tny_msg_get_header (msg);
1568 recipients = modest_tny_msg_header_get_all_recipients_list (header);
1569 g_object_unref (header);