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, strlen(body)));
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
644 modest_tny_msg_find_body_part (TnyMsg *msg, gboolean want_html)
646 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
648 return modest_tny_msg_find_body_part_from_mime_part (TNY_MIME_PART(msg),
653 #define MODEST_TNY_MSG_PARENT_UID "parent-uid"
656 create_reply_forward_mail (TnyMsg *msg, TnyHeader *header, const gchar *from,
657 const gchar *signature, gboolean is_reply,
658 guint type /*ignored*/, GList *attachments)
661 TnyHeader *new_header;
664 TnyMimePart *body = NULL;
665 TnyMimePart *html_body = NULL;
666 ModestFormatter *formatter;
667 gboolean no_text_part;
669 gboolean forward_as_attach = FALSE;
672 g_object_ref (header);
674 header = tny_msg_get_header (msg);
676 /* Get body from original msg. Always look for the text/plain
677 part of the message to create the reply/forwarded mail */
679 body = modest_tny_msg_find_body_part (msg, FALSE);
680 html_body = modest_tny_msg_find_body_part (msg, TRUE);
683 if (modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT,
685 formatter = modest_formatter_new ("text/html", signature);
687 formatter = modest_formatter_new ("text/plain", signature);
690 /* if we don't have a text-part */
691 no_text_part = (!body) || (strcmp (tny_mime_part_get_content_type (body), "text/html")==0);
693 /* when we're reply, include the text part if we have it, or nothing otherwise. */
695 new_msg = modest_formatter_quote (formatter, body, header,
698 if (no_text_part || (html_body && (strcmp (tny_mime_part_get_content_type (html_body), "text/html")==0))) {
699 forward_as_attach = TRUE;
700 new_msg = modest_formatter_attach (formatter, msg, header);
702 forward_as_attach = FALSE;
703 new_msg = modest_formatter_inline (formatter, body, header,
708 g_object_unref (G_OBJECT(formatter));
710 g_object_unref (G_OBJECT(body));
712 g_object_unref (G_OBJECT(html_body));
714 /* Fill the header */
715 new_header = tny_msg_get_header (new_msg);
716 tny_header_set_from (new_header, from);
717 tny_header_set_replyto (new_header, from);
719 /* Change the subject */
720 old_subject = tny_header_dup_subject (header);
722 (gchar *) modest_text_utils_derived_subject (old_subject, is_reply);
723 g_free (old_subject);
724 tny_header_set_subject (new_header, (const gchar *) new_subject);
725 g_free (new_subject);
727 /* get the parent uid, and set it as a gobject property on the new msg */
728 parent_uid = modest_tny_folder_get_header_unique_id (header);
729 g_object_set_data_full (G_OBJECT(new_msg), MODEST_TNY_MSG_PARENT_UID,
732 /* set modest as the X-Mailer
733 * we could this in the platform factory, but then the header
734 * would show up before all the others.
736 tny_mime_part_set_header_pair (TNY_MIME_PART (msg), "X-Mailer", "Modest "
740 g_object_unref (G_OBJECT (new_header));
741 g_object_unref (G_OBJECT (header));
742 /* ugly to unref it here instead of in the calling func */
744 if (!is_reply & !forward_as_attach) {
745 add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, NULL);
752 modest_tny_msg_get_parent_uid (TnyMsg *msg)
754 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
756 return g_object_get_data (G_OBJECT(msg), MODEST_TNY_MSG_PARENT_UID);
759 static gchar *get_signed_protocol (TnyMimePart *part)
761 TnyList *header_pairs;
762 TnyIterator *iterator;
763 gchar *result = NULL;
765 header_pairs = TNY_LIST (tny_simple_list_new ());
766 tny_mime_part_get_header_pairs (part, header_pairs);
767 iterator = tny_list_create_iterator (header_pairs);
769 while (!result && !tny_iterator_is_done (iterator)) {
773 pair = TNY_PAIR (tny_iterator_get_current (iterator));
774 name = tny_pair_get_name (pair);
775 if (name && !g_ascii_strcasecmp (name, "Content-Type")) {
777 s = tny_pair_get_value (pair);
779 s = strstr (s, "protocol=");
785 t = strstr (s, "\"");
789 result = g_strndup (s, t - s);
794 g_object_unref (pair);
795 tny_iterator_next (iterator);
798 g_object_unref (iterator);
799 g_object_unref (header_pairs);
805 modest_tny_msg_get_attachments_parent (TnyMsg *msg)
808 const gchar *content_type;
812 content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
813 if (content_type && !strcmp (content_type, "multipart/signed")) {
814 TnyList *msg_children;
815 TnyIterator *iterator;
816 gchar *signed_protocol;
818 msg_children = TNY_LIST (tny_simple_list_new ());
819 tny_mime_part_get_parts (TNY_MIME_PART (msg), msg_children);
821 iterator = tny_list_create_iterator (msg_children);
822 signed_protocol = get_signed_protocol (TNY_MIME_PART (msg));
824 while (!result && !tny_iterator_is_done (iterator)) {
827 part = TNY_MIME_PART (tny_iterator_get_current (iterator));
828 if (signed_protocol) {
829 const gchar *part_content_type;
831 part_content_type = tny_mime_part_get_content_type (part);
832 if (part_content_type && g_ascii_strcasecmp (part_content_type, signed_protocol)) {
833 result = g_object_ref (part);
836 result = g_object_ref (part);
839 g_object_unref (part);
840 tny_iterator_next (iterator);
843 g_object_unref (iterator);
844 g_free (signed_protocol);
845 g_object_unref (msg_children);
847 if (result == NULL) {
848 result = g_object_ref (msg);
857 add_if_attachment (gpointer data, gpointer user_data)
860 GList **attachments_list;
862 part = TNY_MIME_PART (data);
863 attachments_list = ((GList **) user_data);
865 if ((tny_mime_part_is_attachment (part))||(TNY_IS_MSG (part))) {
866 *attachments_list = g_list_prepend (*attachments_list, part);
872 modest_tny_msg_create_forward_msg (TnyMsg *msg,
874 const gchar *signature,
875 ModestTnyMsgForwardType forward_type)
878 TnyList *parts = NULL;
879 GList *attachments_list = NULL;
880 TnyMimePart *part_to_check = NULL;
882 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
884 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
886 /* Add attachments */
887 parts = TNY_LIST (tny_simple_list_new());
888 tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
889 tny_list_foreach (parts, add_if_attachment, &attachments_list);
891 new_msg = create_reply_forward_mail (msg, NULL, from, signature, FALSE, forward_type,
895 if (attachments_list) {
896 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
897 g_list_free (attachments_list);
899 g_object_unref (G_OBJECT (parts));
900 g_object_unref (part_to_check);
908 count_addresses (const gchar* addresses)
916 if (*addresses == ',' || *addresses == ';')
925 remove_undisclosed_recipients (gchar **recipients)
927 GSList *addresses, *node;
931 g_return_if_fail (recipients);
932 addresses = modest_text_utils_split_addresses_list (*recipients);
935 result = g_string_new ("");
936 for (node = addresses; node != NULL; node = g_slist_next (node)) {
937 const gchar *address = (const gchar *) node->data;
939 if (address && strstr (address, "undisclosed-recipients"))
945 result = g_string_append (result, ", ");
947 result = g_string_append (result, address);
949 g_slist_foreach (addresses, (GFunc)g_free, NULL);
950 g_slist_free (addresses);
952 g_free (*recipients);
953 *recipients = g_string_free (result, FALSE);
957 /* get the new To:, based on the old header,
958 * result is newly allocated or NULL in case of error
961 get_new_to (TnyMsg *msg, TnyHeader *header, const gchar* from,
962 ModestTnyMsgReplyMode reply_mode)
964 const gchar *reply_header = "Reply-To:";
965 const gchar *from_header = "From:";
971 /* according to RFC2369 (http://www.faqs.org/rfcs/rfc2369.html), we
972 * can identify Mailing-List posts by the List-Help header.
973 * for mailing lists, both the Reply-To: and From: should be included
974 * in the new To:; for now, we're ignoring List-Post
976 gchar* list_help = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
978 gboolean is_mailing_list = (list_help != NULL);
982 /* reply to sender, use ReplyTo or From */
983 old_reply_to = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
985 old_from = tny_header_dup_from (header);
987 if (!old_from && !old_reply_to) {
988 g_debug ("%s: failed to get either Reply-To: or From: from header",
993 /* Prevent DoS attacks caused by malformed emails */
995 gchar *tmp = old_from;
996 old_from = modest_text_utils_get_secure_header ((const gchar *) tmp, from_header);
1000 gchar *tmp = old_reply_to;
1001 old_reply_to = modest_text_utils_get_secure_header ((const gchar *) tmp, reply_header);
1005 /* for mailing lists, use both Reply-To and From if we did a
1008 if (is_mailing_list && reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL &&
1009 old_reply_to && old_from && strcmp (old_from, old_reply_to) != 0)
1010 new_to = g_strjoin (",", old_reply_to, old_from, NULL);
1012 /* otherwise use either Reply-To: (preferred) or From: */
1013 new_to = g_strdup (old_reply_to ? old_reply_to : old_from);
1015 g_free (old_reply_to);
1017 /* in case of ReplyAll, we need to add the Recipients in the old To: */
1018 if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1019 gchar *old_to = tny_header_dup_to (header);
1021 g_debug ("%s: no To: address found in source mail",
1024 /* append the old To: */
1025 gchar *tmp = g_strjoin (",", new_to, old_to, NULL);
1031 /* remove duplicate entries */
1032 gchar *tmp = modest_text_utils_remove_duplicate_addresses (new_to);
1036 /* now, strip me (the new From:) from the new_to, but only if
1037 * there are >1 addresses there */
1038 if (count_addresses (new_to) > 1) {
1039 gchar *tmp = modest_text_utils_remove_address (new_to, from);
1045 tmp = modest_text_utils_simplify_recipients (new_to);
1046 remove_undisclosed_recipients (&tmp);
1054 /* get the new Cc:, based on the old header,
1055 * result is newly allocated or NULL in case of error */
1057 get_new_cc (TnyHeader *header, const gchar* from, const gchar *new_to)
1059 gchar *old_cc, *result, *dup;
1061 old_cc = tny_header_dup_cc (header);
1065 /* remove me (the new From:) from the Cc: list */
1066 dup = modest_text_utils_remove_address (old_cc, from);
1069 gchar **to_parts, **current;
1071 to_parts = g_strsplit (new_to, ",", 0);
1072 for (current = to_parts; current && *current != '\0'; current++) {
1075 dup2 = modest_text_utils_remove_address (dup, g_strstrip (*current));
1079 g_strfreev (to_parts);
1082 result = modest_text_utils_remove_duplicate_addresses (dup);
1085 result = modest_text_utils_simplify_recipients (dup);
1086 remove_undisclosed_recipients (&result);
1093 modest_tny_msg_get_references (TnyMsg *msg, gchar **message_id, gchar **references, gchar **in_reply_to)
1096 TnyIterator *iterator;
1097 gchar *l_message_id;
1098 gchar *l_references;
1099 gchar *l_in_reply_to;
1101 g_return_if_fail (TNY_IS_MSG (msg));
1103 l_message_id = NULL;
1104 l_references = NULL;
1105 l_in_reply_to = NULL;
1107 headers = TNY_LIST (tny_simple_list_new ());
1108 tny_mime_part_get_header_pairs (TNY_MIME_PART (msg), headers);
1110 iterator = tny_list_create_iterator (headers);
1111 while (!tny_iterator_is_done (iterator)) {
1115 pair = TNY_PAIR (tny_iterator_get_current (iterator));
1116 name = tny_pair_get_name (pair);
1117 if (!g_ascii_strcasecmp (name, "References")) {
1118 if (l_references) g_free (l_references);
1119 l_references = g_strdup (tny_pair_get_value (pair));
1120 } else if (!g_ascii_strcasecmp (name, "In-Reply-To")) {
1121 if (l_in_reply_to) g_free (l_in_reply_to);
1122 l_in_reply_to = g_strdup (tny_pair_get_value (pair));
1123 } else if (!g_ascii_strcasecmp (name, "Message-ID")) {
1124 if (l_message_id) g_free (l_message_id);
1125 l_message_id = g_strdup (tny_pair_get_value (pair));
1128 g_object_unref (pair);
1129 tny_iterator_next (iterator);
1132 g_object_unref (iterator);
1133 g_object_unref (headers);
1136 *message_id = l_message_id;
1138 g_free (l_message_id);
1142 *in_reply_to = l_in_reply_to;
1144 g_free (l_in_reply_to);
1148 *references = l_references;
1150 g_free (l_references);
1155 set_references (TnyMsg *reply_msg, TnyMsg *original_msg)
1157 gchar *orig_references, *orig_in_reply_to, *orig_message_id;
1158 gchar *references, *in_reply_to;
1160 modest_tny_msg_get_references (original_msg, &orig_message_id, &orig_references, &orig_in_reply_to);
1165 if (orig_message_id)
1166 in_reply_to = g_strdup (orig_message_id);
1168 if (orig_references) {
1169 if (orig_message_id)
1170 references = g_strconcat (orig_references, "\n ", orig_message_id, NULL);
1172 references = g_strdup (orig_references);
1174 } else if (orig_in_reply_to) {
1175 if (orig_message_id)
1176 references = g_strconcat (orig_in_reply_to, "\n ", orig_message_id, NULL);
1178 references = g_strdup (orig_in_reply_to);
1179 } else if (orig_message_id) {
1180 references = g_strdup (orig_message_id);
1183 g_free (orig_references);
1184 g_free (orig_in_reply_to);
1185 g_free (orig_message_id);
1188 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "In-Reply-To", in_reply_to);
1191 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "References", references);
1196 modest_tny_msg_create_reply_msg (TnyMsg *msg,
1199 const gchar *signature,
1200 ModestTnyMsgReplyType reply_type,
1201 ModestTnyMsgReplyMode reply_mode)
1203 TnyMsg *new_msg = NULL;
1204 TnyHeader *new_header;
1205 gchar *new_to = NULL;
1206 TnyList *parts = NULL;
1207 GList *attachments_list = NULL;
1208 TnyMimePart *part_to_check = NULL;
1210 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1212 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
1214 parts = TNY_LIST (tny_simple_list_new());
1215 tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
1216 tny_list_foreach (parts, add_if_attachment, &attachments_list);
1218 new_msg = create_reply_forward_mail (msg, header, from, signature, TRUE, reply_type,
1221 set_references (new_msg, msg);
1222 if (attachments_list != NULL) {
1223 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
1224 g_list_free (attachments_list);
1226 g_object_unref (parts);
1228 /* Fill the header */
1230 g_object_ref (header);
1232 header = tny_msg_get_header (msg);
1235 new_header = tny_msg_get_header(new_msg);
1236 new_to = get_new_to (msg, header, from, reply_mode);
1238 g_debug ("%s: failed to get new To:", __FUNCTION__);
1240 tny_header_set_to (new_header, new_to);
1243 if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1244 gchar *new_cc = get_new_cc (header, from, new_to);
1246 tny_header_set_cc (new_header, new_cc);
1255 g_object_unref (G_OBJECT (new_header));
1256 g_object_unref (G_OBJECT (header));
1257 g_object_unref (G_OBJECT (part_to_check));
1264 is_ascii(const gchar *s)
1269 if (s[0] & 128 || s[0] < 32)
1277 get_content_type(const gchar *s)
1281 type = g_string_new("text/plain");
1283 if (g_utf8_validate(s, -1, NULL)) {
1284 g_string_append(type, "; charset=\"utf-8\"");
1286 /* it should be impossible to reach this, but better safe than sorry */
1287 g_debug("invalid utf8 in message");
1288 g_string_append(type, "; charset=\"latin1\"");
1291 return g_string_free(type, FALSE);
1295 modest_tny_msg_estimate_size (const gchar *plain_body, const gchar *html_body,
1296 guint64 parts_count,
1301 /* estimation of headers size */
1304 /* We add a 20% of size due to the increase in 7bit encoding */
1306 result += strlen (plain_body) * 120 / 100;
1309 result += strlen (html_body) * 120 / 100;
1312 /* 256 bytes per additional part because of their headers */
1313 result += parts_count * 256;
1315 /* 150% of increase per encoding */
1316 result += parts_size * 3 / 2;
1322 modest_tny_msg_header_get_all_recipients_list (TnyHeader *header)
1324 GSList *recipients = NULL;
1325 gchar *from = NULL, *to = NULL, *cc = NULL, *bcc = NULL;
1326 gchar *after_remove, *joined;
1331 from = tny_header_dup_from (header);
1332 to = tny_header_dup_to (header);
1333 cc = tny_header_dup_cc (header);
1334 bcc = tny_header_dup_bcc (header);
1336 joined = modest_text_utils_join_addresses (from, to, cc, bcc);
1337 after_remove = modest_text_utils_remove_duplicate_addresses (joined);
1340 recipients = modest_text_utils_split_addresses_list (after_remove);
1341 g_free (after_remove);
1356 modest_tny_msg_get_all_recipients_list (TnyMsg *msg)
1358 TnyHeader *header = NULL;
1359 GSList *recipients = NULL;
1364 header = tny_msg_get_header (msg);
1368 recipients = modest_tny_msg_header_get_all_recipients_list (header);
1369 g_object_unref (header);