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 gchar *subject_prefix;
668 gboolean no_text_part;
670 gboolean forward_as_attach = FALSE;
673 g_object_ref (header);
675 header = tny_msg_get_header (msg);
677 /* Get body from original msg. Always look for the text/plain
678 part of the message to create the reply/forwarded mail */
680 body = modest_tny_msg_find_body_part (msg, FALSE);
681 html_body = modest_tny_msg_find_body_part (msg, TRUE);
684 if (modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT,
686 formatter = modest_formatter_new ("text/html", signature);
688 formatter = modest_formatter_new ("text/plain", signature);
691 /* if we don't have a text-part */
692 no_text_part = (!body) || (strcmp (tny_mime_part_get_content_type (body), "text/html")==0);
694 /* when we're reply, include the text part if we have it, or nothing otherwise. */
696 new_msg = modest_formatter_quote (formatter, body, header,
699 if (no_text_part || (html_body && (strcmp (tny_mime_part_get_content_type (html_body), "text/html")==0))) {
700 forward_as_attach = TRUE;
701 new_msg = modest_formatter_attach (formatter, msg, header);
703 forward_as_attach = FALSE;
704 new_msg = modest_formatter_inline (formatter, body, header,
709 g_object_unref (G_OBJECT(formatter));
711 g_object_unref (G_OBJECT(body));
713 g_object_unref (G_OBJECT(html_body));
715 /* Fill the header */
716 new_header = tny_msg_get_header (new_msg);
717 tny_header_set_from (new_header, from);
718 tny_header_set_replyto (new_header, from);
720 /* Change the subject */
722 subject_prefix = g_strconcat (_("mail_va_re"), ":", NULL);
724 subject_prefix = g_strconcat (_("mail_va_fw"), ":", NULL);
725 old_subject = tny_header_dup_subject (header);
727 (gchar *) modest_text_utils_derived_subject (old_subject,
729 g_free (old_subject);
730 g_free (subject_prefix);
731 tny_header_set_subject (new_header, (const gchar *) new_subject);
732 g_free (new_subject);
734 /* get the parent uid, and set it as a gobject property on the new msg */
735 parent_uid = modest_tny_folder_get_header_unique_id (header);
736 g_object_set_data_full (G_OBJECT(new_msg), MODEST_TNY_MSG_PARENT_UID,
739 /* set modest as the X-Mailer
740 * we could this in the platform factory, but then the header
741 * would show up before all the others.
743 tny_mime_part_set_header_pair (TNY_MIME_PART (msg), "X-Mailer", "Modest "
747 g_object_unref (G_OBJECT (new_header));
748 g_object_unref (G_OBJECT (header));
749 /* ugly to unref it here instead of in the calling func */
751 if (!is_reply & !forward_as_attach) {
752 add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, NULL);
759 modest_tny_msg_get_parent_uid (TnyMsg *msg)
761 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
763 return g_object_get_data (G_OBJECT(msg), MODEST_TNY_MSG_PARENT_UID);
766 static gchar *get_signed_protocol (TnyMimePart *part)
768 TnyList *header_pairs;
769 TnyIterator *iterator;
770 gchar *result = NULL;
772 header_pairs = TNY_LIST (tny_simple_list_new ());
773 tny_mime_part_get_header_pairs (part, header_pairs);
774 iterator = tny_list_create_iterator (header_pairs);
776 while (!result && !tny_iterator_is_done (iterator)) {
780 pair = TNY_PAIR (tny_iterator_get_current (iterator));
781 name = tny_pair_get_name (pair);
782 if (name && !g_ascii_strcasecmp (name, "Content-Type")) {
784 s = tny_pair_get_value (pair);
786 s = strstr (s, "protocol=");
792 t = strstr (s, "\"");
796 result = g_strndup (s, t - s);
801 g_object_unref (pair);
802 tny_iterator_next (iterator);
805 g_object_unref (iterator);
806 g_object_unref (header_pairs);
812 modest_tny_msg_get_attachments_parent (TnyMsg *msg)
815 const gchar *content_type;
819 content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
820 if (content_type && !strcmp (content_type, "multipart/signed")) {
821 TnyList *msg_children;
822 TnyIterator *iterator;
823 gchar *signed_protocol;
825 msg_children = TNY_LIST (tny_simple_list_new ());
826 tny_mime_part_get_parts (TNY_MIME_PART (msg), msg_children);
828 iterator = tny_list_create_iterator (msg_children);
829 signed_protocol = get_signed_protocol (TNY_MIME_PART (msg));
831 while (!result && !tny_iterator_is_done (iterator)) {
834 part = TNY_MIME_PART (tny_iterator_get_current (iterator));
835 if (signed_protocol) {
836 const gchar *part_content_type;
838 part_content_type = tny_mime_part_get_content_type (part);
839 if (part_content_type && g_ascii_strcasecmp (part_content_type, signed_protocol)) {
840 result = g_object_ref (part);
843 result = g_object_ref (part);
846 g_object_unref (part);
847 tny_iterator_next (iterator);
850 g_object_unref (iterator);
851 g_free (signed_protocol);
852 g_object_unref (msg_children);
854 if (result == NULL) {
855 result = g_object_ref (msg);
864 add_if_attachment (gpointer data, gpointer user_data)
867 GList **attachments_list;
869 part = TNY_MIME_PART (data);
870 attachments_list = ((GList **) user_data);
872 if ((tny_mime_part_is_attachment (part))||(TNY_IS_MSG (part))) {
873 *attachments_list = g_list_prepend (*attachments_list, part);
879 modest_tny_msg_create_forward_msg (TnyMsg *msg,
881 const gchar *signature,
882 ModestTnyMsgForwardType forward_type)
885 TnyList *parts = NULL;
886 GList *attachments_list = NULL;
887 TnyMimePart *part_to_check = NULL;
889 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
891 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
893 /* Add attachments */
894 parts = TNY_LIST (tny_simple_list_new());
895 tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
896 tny_list_foreach (parts, add_if_attachment, &attachments_list);
898 new_msg = create_reply_forward_mail (msg, NULL, from, signature, FALSE, forward_type,
902 if (attachments_list) {
903 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
904 g_list_free (attachments_list);
906 g_object_unref (G_OBJECT (parts));
907 g_object_unref (part_to_check);
915 count_addresses (const gchar* addresses)
923 if (*addresses == ',' || *addresses == ';')
932 remove_undisclosed_recipients (gchar **recipients)
934 GSList *addresses, *node;
938 g_return_if_fail (recipients);
939 addresses = modest_text_utils_split_addresses_list (*recipients);
942 result = g_string_new ("");
943 for (node = addresses; node != NULL; node = g_slist_next (node)) {
944 const gchar *address = (const gchar *) node->data;
946 if (address && strstr (address, "undisclosed-recipients"))
952 result = g_string_append (result, ", ");
954 result = g_string_append (result, address);
956 g_slist_foreach (addresses, (GFunc)g_free, NULL);
957 g_slist_free (addresses);
959 g_free (*recipients);
960 *recipients = g_string_free (result, FALSE);
964 /* get the new To:, based on the old header,
965 * result is newly allocated or NULL in case of error
968 get_new_to (TnyMsg *msg, TnyHeader *header, const gchar* from,
969 ModestTnyMsgReplyMode reply_mode)
971 const gchar *reply_header = "Reply-To:";
972 const gchar *from_header = "From:";
978 /* according to RFC2369 (http://www.faqs.org/rfcs/rfc2369.html), we
979 * can identify Mailing-List posts by the List-Help header.
980 * for mailing lists, both the Reply-To: and From: should be included
981 * in the new To:; for now, we're ignoring List-Post
983 gchar* list_help = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
985 gboolean is_mailing_list = (list_help != NULL);
989 /* reply to sender, use ReplyTo or From */
990 old_reply_to = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg),
992 old_from = tny_header_dup_from (header);
994 if (!old_from && !old_reply_to) {
995 g_debug ("%s: failed to get either Reply-To: or From: from header",
1000 /* Prevent DoS attacks caused by malformed emails */
1002 gchar *tmp = old_from;
1003 old_from = modest_text_utils_get_secure_header ((const gchar *) tmp, from_header);
1007 gchar *tmp = old_reply_to;
1008 old_reply_to = modest_text_utils_get_secure_header ((const gchar *) tmp, reply_header);
1012 /* for mailing lists, use both Reply-To and From if we did a
1015 if (is_mailing_list && reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL &&
1016 old_reply_to && old_from && strcmp (old_from, old_reply_to) != 0)
1017 new_to = g_strjoin (",", old_reply_to, old_from, NULL);
1019 /* otherwise use either Reply-To: (preferred) or From: */
1020 new_to = g_strdup (old_reply_to ? old_reply_to : old_from);
1022 g_free (old_reply_to);
1024 /* in case of ReplyAll, we need to add the Recipients in the old To: */
1025 if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1026 gchar *old_to = tny_header_dup_to (header);
1028 g_debug ("%s: no To: address found in source mail",
1031 /* append the old To: */
1032 gchar *tmp = g_strjoin (",", new_to, old_to, NULL);
1038 /* remove duplicate entries */
1039 gchar *tmp = modest_text_utils_remove_duplicate_addresses (new_to);
1043 /* now, strip me (the new From:) from the new_to, but only if
1044 * there are >1 addresses there */
1045 if (count_addresses (new_to) > 1) {
1046 gchar *tmp = modest_text_utils_remove_address (new_to, from);
1052 tmp = modest_text_utils_simplify_recipients (new_to);
1053 remove_undisclosed_recipients (&tmp);
1061 /* get the new Cc:, based on the old header,
1062 * result is newly allocated or NULL in case of error */
1064 get_new_cc (TnyHeader *header, const gchar* from, const gchar *new_to)
1066 gchar *old_cc, *result, *dup;
1068 old_cc = tny_header_dup_cc (header);
1072 /* remove me (the new From:) from the Cc: list */
1073 dup = modest_text_utils_remove_address (old_cc, from);
1076 gchar **to_parts, **current;
1078 to_parts = g_strsplit (new_to, ",", 0);
1079 for (current = to_parts; current && *current != '\0'; current++) {
1082 dup2 = modest_text_utils_remove_address (dup, g_strstrip (*current));
1086 g_strfreev (to_parts);
1089 result = modest_text_utils_remove_duplicate_addresses (dup);
1092 result = modest_text_utils_simplify_recipients (dup);
1093 remove_undisclosed_recipients (&result);
1100 modest_tny_msg_get_references (TnyMsg *msg, gchar **message_id, gchar **references, gchar **in_reply_to)
1103 TnyIterator *iterator;
1104 gchar *l_message_id;
1105 gchar *l_references;
1106 gchar *l_in_reply_to;
1108 g_return_if_fail (TNY_IS_MSG (msg));
1110 l_message_id = NULL;
1111 l_references = NULL;
1112 l_in_reply_to = NULL;
1114 headers = TNY_LIST (tny_simple_list_new ());
1115 tny_mime_part_get_header_pairs (TNY_MIME_PART (msg), headers);
1117 iterator = tny_list_create_iterator (headers);
1118 while (!tny_iterator_is_done (iterator)) {
1122 pair = TNY_PAIR (tny_iterator_get_current (iterator));
1123 name = tny_pair_get_name (pair);
1124 if (!g_ascii_strcasecmp (name, "References")) {
1125 if (l_references) g_free (l_references);
1126 l_references = g_strdup (tny_pair_get_value (pair));
1127 } else if (!g_ascii_strcasecmp (name, "In-Reply-To")) {
1128 if (l_in_reply_to) g_free (l_in_reply_to);
1129 l_in_reply_to = g_strdup (tny_pair_get_value (pair));
1130 } else if (!g_ascii_strcasecmp (name, "Message-ID")) {
1131 if (l_message_id) g_free (l_message_id);
1132 l_message_id = g_strdup (tny_pair_get_value (pair));
1135 g_object_unref (pair);
1136 tny_iterator_next (iterator);
1139 g_object_unref (iterator);
1140 g_object_unref (headers);
1143 *message_id = l_message_id;
1145 g_free (l_message_id);
1149 *in_reply_to = l_in_reply_to;
1151 g_free (l_in_reply_to);
1155 *references = l_references;
1157 g_free (l_references);
1162 set_references (TnyMsg *reply_msg, TnyMsg *original_msg)
1164 gchar *orig_references, *orig_in_reply_to, *orig_message_id;
1165 gchar *references, *in_reply_to;
1167 modest_tny_msg_get_references (original_msg, &orig_message_id, &orig_references, &orig_in_reply_to);
1172 if (orig_message_id)
1173 in_reply_to = g_strdup (orig_message_id);
1175 if (orig_references) {
1176 if (orig_message_id)
1177 references = g_strconcat (orig_references, "\n ", orig_message_id, NULL);
1179 references = g_strdup (orig_references);
1181 } else if (orig_in_reply_to) {
1182 if (orig_message_id)
1183 references = g_strconcat (orig_in_reply_to, "\n ", orig_message_id, NULL);
1185 references = g_strdup (orig_in_reply_to);
1186 } else if (orig_message_id) {
1187 references = g_strdup (orig_message_id);
1190 g_free (orig_references);
1191 g_free (orig_in_reply_to);
1192 g_free (orig_message_id);
1195 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "In-Reply-To", in_reply_to);
1198 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "References", references);
1203 modest_tny_msg_create_reply_msg (TnyMsg *msg,
1206 const gchar *signature,
1207 ModestTnyMsgReplyType reply_type,
1208 ModestTnyMsgReplyMode reply_mode)
1210 TnyMsg *new_msg = NULL;
1211 TnyHeader *new_header;
1212 gchar *new_to = NULL;
1213 TnyList *parts = NULL;
1214 GList *attachments_list = NULL;
1215 TnyMimePart *part_to_check = NULL;
1217 g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1219 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
1221 parts = TNY_LIST (tny_simple_list_new());
1222 tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
1223 tny_list_foreach (parts, add_if_attachment, &attachments_list);
1225 new_msg = create_reply_forward_mail (msg, header, from, signature, TRUE, reply_type,
1228 set_references (new_msg, msg);
1229 if (attachments_list != NULL) {
1230 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
1231 g_list_free (attachments_list);
1233 g_object_unref (parts);
1235 /* Fill the header */
1237 g_object_ref (header);
1239 header = tny_msg_get_header (msg);
1242 new_header = tny_msg_get_header(new_msg);
1243 new_to = get_new_to (msg, header, from, reply_mode);
1245 g_debug ("%s: failed to get new To:", __FUNCTION__);
1247 tny_header_set_to (new_header, new_to);
1250 if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1251 gchar *new_cc = get_new_cc (header, from, new_to);
1253 tny_header_set_cc (new_header, new_cc);
1262 g_object_unref (G_OBJECT (new_header));
1263 g_object_unref (G_OBJECT (header));
1264 g_object_unref (G_OBJECT (part_to_check));
1271 is_ascii(const gchar *s)
1276 if (s[0] & 128 || s[0] < 32)
1284 get_content_type(const gchar *s)
1288 type = g_string_new("text/plain");
1290 if (g_utf8_validate(s, -1, NULL)) {
1291 g_string_append(type, "; charset=\"utf-8\"");
1293 /* it should be impossible to reach this, but better safe than sorry */
1294 g_debug("invalid utf8 in message");
1295 g_string_append(type, "; charset=\"latin1\"");
1298 return g_string_free(type, FALSE);
1302 modest_tny_msg_estimate_size (const gchar *plain_body, const gchar *html_body,
1303 guint64 parts_count,
1308 /* estimation of headers size */
1311 /* We add a 20% of size due to the increase in 7bit encoding */
1313 result += strlen (plain_body) * 120 / 100;
1316 result += strlen (html_body) * 120 / 100;
1319 /* 256 bytes per additional part because of their headers */
1320 result += parts_count * 256;
1322 /* 150% of increase per encoding */
1323 result += parts_size * 3 / 2;
1329 modest_tny_msg_header_get_all_recipients_list (TnyHeader *header)
1331 GSList *recipients = NULL;
1332 gchar *from = NULL, *to = NULL, *cc = NULL, *bcc = NULL;
1333 gchar *after_remove;
1335 gboolean add_separator = TRUE;
1340 buffer = g_string_new ("");
1342 from = tny_header_dup_from (header);
1343 to = tny_header_dup_to (header);
1344 cc = tny_header_dup_cc (header);
1345 bcc = tny_header_dup_bcc (header);
1349 buffer = g_string_append (buffer, from);
1350 add_separator = TRUE;
1354 buffer = g_string_append (buffer, "; ");
1356 add_separator = TRUE;
1358 buffer = g_string_append (buffer, to);
1362 buffer = g_string_append (buffer, "; ");
1364 add_separator = TRUE;
1366 buffer = g_string_append (buffer, cc);
1370 buffer = g_string_append (buffer, "; ");
1372 add_separator = TRUE;
1374 buffer = g_string_append (buffer, bcc);
1377 after_remove = modest_text_utils_remove_duplicate_addresses (buffer->str);
1378 g_string_free (buffer, TRUE);
1380 recipients = modest_text_utils_split_addresses_list (after_remove);
1381 g_free (after_remove);
1396 modest_tny_msg_get_all_recipients_list (TnyMsg *msg)
1398 TnyHeader *header = NULL;
1399 GSList *recipients = NULL;
1404 header = tny_msg_get_header (msg);
1408 recipients = modest_tny_msg_header_get_all_recipients_list (header);
1409 g_object_unref (header);