1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * This program is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation.
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * You should have received a copy of the GNU Lesser General Public License
13 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 * Ettore Perazzoli (ettore@ximian.com)
18 * Jeffrey Stedfast (fejj@ximian.com)
19 * Miguel de Icaza (miguel@ximian.com)
20 * Radek Doulik (rodo@ximian.com)
22 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
32 #include <sys/types.h>
38 #include <enchant/enchant.h>
40 #include "e-composer-from-header.h"
41 #include "e-composer-private.h"
43 #include <em-format/e-mail-part.h>
44 #include <em-format/e-mail-parser.h>
45 #include <em-format/e-mail-formatter-quote.h>
47 #include <shell/e-shell.h>
49 #include <libemail-engine/libemail-engine.h>
51 typedef struct _AsyncContext AsyncContext
;
53 struct _AsyncContext
{
56 CamelMimeMessage
*message
;
57 CamelDataWrapper
*top_level_part
;
58 CamelDataWrapper
*text_plain_part
;
61 CamelSession
*session
;
62 CamelInternetAddress
*from
;
64 CamelTransferEncoding plain_encoding
;
65 GtkPrintOperationAction print_action
;
67 GPtrArray
*recipients
;
69 guint skip_content
: 1;
70 guint need_thread
: 1;
72 guint pgp_encrypt
: 1;
74 guint smime_encrypt
: 1;
77 /* Flags for building a message. */
79 COMPOSER_FLAG_HTML_CONTENT
= 1 << 0,
80 COMPOSER_FLAG_SAVE_OBJECT_DATA
= 1 << 1,
81 COMPOSER_FLAG_PRIORITIZE_MESSAGE
= 1 << 2,
82 COMPOSER_FLAG_REQUEST_READ_RECEIPT
= 1 << 3,
83 COMPOSER_FLAG_PGP_SIGN
= 1 << 4,
84 COMPOSER_FLAG_PGP_ENCRYPT
= 1 << 5,
85 COMPOSER_FLAG_SMIME_SIGN
= 1 << 6,
86 COMPOSER_FLAG_SMIME_ENCRYPT
= 1 << 7,
87 COMPOSER_FLAG_HTML_MODE
= 1 << 8,
88 COMPOSER_FLAG_SAVE_DRAFT
= 1 << 9
109 DND_TARGET_TYPE_TEXT_URI_LIST
,
110 DND_TARGET_TYPE_MOZILLA_URL
,
111 DND_TARGET_TYPE_TEXT_HTML
,
112 DND_TARGET_TYPE_UTF8_STRING
,
113 DND_TARGET_TYPE_TEXT_PLAIN
,
114 DND_TARGET_TYPE_STRING
,
115 DND_TARGET_TYPE_TEXT_PLAIN_UTF8
118 static GtkTargetEntry drag_dest_targets
[] = {
119 { (gchar
*) "text/uri-list", 0, DND_TARGET_TYPE_TEXT_URI_LIST
},
120 { (gchar
*) "_NETSCAPE_URL", 0, DND_TARGET_TYPE_MOZILLA_URL
},
121 { (gchar
*) "text/html", 0, DND_TARGET_TYPE_TEXT_HTML
},
122 { (gchar
*) "UTF8_STRING", 0, DND_TARGET_TYPE_UTF8_STRING
},
123 { (gchar
*) "text/plain", 0, DND_TARGET_TYPE_TEXT_PLAIN
},
124 { (gchar
*) "STRING", 0, DND_TARGET_TYPE_STRING
},
125 { (gchar
*) "text/plain;charset=utf-8", 0, DND_TARGET_TYPE_TEXT_PLAIN_UTF8
},
128 static guint signals
[LAST_SIGNAL
];
130 /* used by e_msg_composer_add_message_attachments () */
131 static void add_attachments_from_multipart (EMsgComposer
*composer
,
132 CamelMultipart
*multipart
,
133 gboolean just_inlines
,
136 /* used by e_msg_composer_new_with_message () */
137 static void handle_multipart (EMsgComposer
*composer
,
138 CamelMultipart
*multipart
,
139 gboolean keep_signature
,
140 GCancellable
*cancellable
,
142 static void handle_multipart_alternative (EMsgComposer
*composer
,
143 CamelMultipart
*multipart
,
144 gboolean keep_signature
,
145 GCancellable
*cancellable
,
147 static void handle_multipart_encrypted (EMsgComposer
*composer
,
148 CamelMimePart
*multipart
,
149 gboolean keep_signature
,
150 GCancellable
*cancellable
,
152 static void handle_multipart_signed (EMsgComposer
*composer
,
153 CamelMultipart
*multipart
,
154 gboolean keep_signature
,
155 GCancellable
*cancellable
,
158 G_DEFINE_TYPE_WITH_CODE (
162 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE
, NULL
))
165 async_context_free (AsyncContext
*context
)
167 if (context
->activity
!= NULL
)
168 g_object_unref (context
->activity
);
170 if (context
->message
!= NULL
)
171 g_object_unref (context
->message
);
173 if (context
->top_level_part
!= NULL
)
174 g_object_unref (context
->top_level_part
);
176 if (context
->text_plain_part
!= NULL
)
177 g_object_unref (context
->text_plain_part
);
179 if (context
->source
!= NULL
)
180 g_object_unref (context
->source
);
182 if (context
->session
!= NULL
)
183 g_object_unref (context
->session
);
185 if (context
->from
!= NULL
)
186 g_object_unref (context
->from
);
188 if (context
->recipients
!= NULL
)
189 g_ptr_array_free (context
->recipients
, TRUE
);
191 g_slice_free (AsyncContext
, context
);
198 * Converts a mime part's contents into html text. If @credits is given,
199 * then it will be used as an attribution string, and the
200 * content will be cited. Otherwise no citation or attribution
203 * Return Value: The part in displayable html format.
206 emcu_part_to_html (EMsgComposer
*composer
,
209 gboolean keep_signature
,
210 GCancellable
*cancellable
)
212 CamelSession
*session
;
213 GOutputStream
*stream
;
216 EMailFormatter
*formatter
;
217 EMailPartList
*part_list
;
221 gsize n_bytes_written
= 0;
222 GQueue queue
= G_QUEUE_INIT
;
224 shell
= e_shell_get_default ();
225 window
= e_shell_get_active_window (shell
);
227 session
= e_msg_composer_ref_session (composer
);
229 part_list
= e_mail_part_list_new (NULL
, NULL
, NULL
);
231 part_id
= g_string_sized_new (0);
232 parser
= e_mail_parser_new (session
);
233 e_mail_parser_parse_part (
234 parser
, part
, part_id
, cancellable
, &queue
);
235 while (!g_queue_is_empty (&queue
)) {
236 EMailPart
*mail_part
= g_queue_pop_head (&queue
);
238 if (!e_mail_part_get_is_attachment (mail_part
) &&
239 !mail_part
->is_hidden
)
240 e_mail_part_list_add_part (part_list
, mail_part
);
242 g_object_unref (mail_part
);
244 g_string_free (part_id
, TRUE
);
245 g_object_unref (parser
);
246 g_object_unref (session
);
248 if (e_mail_part_list_is_empty (part_list
)) {
249 g_object_unref (part_list
);
253 stream
= g_memory_output_stream_new_resizable ();
255 formatter
= e_mail_formatter_quote_new (
256 NULL
, keep_signature
? E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG
: 0);
257 e_mail_formatter_update_style (
259 gtk_widget_get_state_flags (GTK_WIDGET (window
)));
261 e_mail_formatter_format_sync (
262 formatter
, part_list
, stream
,
263 0, E_MAIL_FORMATTER_MODE_PRINTING
, cancellable
);
265 g_object_unref (formatter
);
266 g_object_unref (part_list
);
268 g_output_stream_write_all (stream
, "", 1, &n_bytes_written
, NULL
, NULL
);
270 g_output_stream_close (stream
, NULL
, NULL
);
272 text
= g_memory_output_stream_steal_data (
273 G_MEMORY_OUTPUT_STREAM (stream
));
276 *len
= strlen (text
);
278 g_object_unref (stream
);
283 /* copy of mail_tool_remove_xevolution_headers */
284 static struct _camel_header_raw
*
285 emcu_remove_xevolution_headers (CamelMimeMessage
*message
)
287 struct _camel_header_raw
*scan
, *list
= NULL
;
289 for (scan
= ((CamelMimePart
*) message
)->headers
; scan
; scan
= scan
->next
)
290 if (!strncmp (scan
->name
, "X-Evolution", 11))
291 camel_header_raw_append (&list
, scan
->name
, scan
->value
, scan
->offset
);
293 for (scan
= list
; scan
; scan
= scan
->next
)
294 camel_medium_remove_header ((CamelMedium
*) message
, scan
->name
);
299 static EDestination
**
300 destination_list_to_vector_sized (GList
*list
,
303 EDestination
**destv
;
307 n
= g_list_length (list
);
312 destv
= g_new (EDestination
*, n
+ 1);
313 while (list
!= NULL
&& i
< n
) {
314 destv
[i
] = E_DESTINATION (list
->data
);
317 list
= g_list_next (list
);
324 static EDestination
**
325 destination_list_to_vector (GList
*list
)
327 return destination_list_to_vector_sized (list
, -1);
333 text_requires_quoted_printable (const gchar
*text
,
345 if (len
>= 5 && strncmp (text
, "From ", 5) == 0)
348 for (p
= text
, pos
= 0; pos
+ 6 <= len
; pos
++, p
++) {
349 if (*p
== '\n' && strncmp (p
+ 1, "From ", 5) == 0)
356 static CamelTransferEncoding
357 best_encoding (GByteArray
*buf
,
358 const gchar
*charset
)
360 gchar
*in
, *out
, outbuf
[256], *ch
;
362 gint status
, count
= 0;
368 cd
= camel_iconv_open (charset
, "utf-8");
369 if (cd
== (iconv_t
) -1)
372 in
= (gchar
*) buf
->data
;
376 outlen
= sizeof (outbuf
);
377 status
= camel_iconv (cd
, (const gchar
**) &in
, &inlen
, &out
, &outlen
);
378 for (ch
= out
- 1; ch
>= outbuf
; ch
--) {
379 if ((guchar
) *ch
> 127)
382 } while (status
== (gsize
) -1 && errno
== E2BIG
);
383 camel_iconv_close (cd
);
385 if (status
== (gsize
) -1 || status
> 0)
388 if ((count
== 0) && (buf
->len
< LINE_LEN
) &&
389 !text_requires_quoted_printable (
390 (const gchar
*) buf
->data
, buf
->len
))
391 return CAMEL_TRANSFER_ENCODING_7BIT
;
392 else if (count
<= buf
->len
* 0.17)
393 return CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
;
395 return CAMEL_TRANSFER_ENCODING_BASE64
;
399 best_charset (GByteArray
*buf
,
400 const gchar
*default_charset
,
401 CamelTransferEncoding
*encoding
)
403 const gchar
*charset
;
405 /* First try US-ASCII */
406 *encoding
= best_encoding (buf
, "US-ASCII");
407 if (*encoding
== CAMEL_TRANSFER_ENCODING_7BIT
)
410 /* Next try the user-specified charset for this message */
411 *encoding
= best_encoding (buf
, default_charset
);
413 return g_strdup (default_charset
);
415 /* Now try the user's default charset from the mail config */
416 charset
= e_composer_get_default_charset ();
417 *encoding
= best_encoding (buf
, charset
);
419 return g_strdup (charset
);
421 /* Try to find something that will work */
422 charset
= camel_charset_best (
423 (const gchar
*) buf
->data
, buf
->len
);
424 if (charset
== NULL
) {
425 *encoding
= CAMEL_TRANSFER_ENCODING_7BIT
;
429 *encoding
= best_encoding (buf
, charset
);
431 return g_strdup (charset
);
434 /* These functions builds a CamelMimeMessage for the message that the user has
435 * composed in 'composer'.
439 set_recipients_from_destv (CamelMimeMessage
*msg
,
440 EDestination
**to_destv
,
441 EDestination
**cc_destv
,
442 EDestination
**bcc_destv
,
445 CamelInternetAddress
*to_addr
;
446 CamelInternetAddress
*cc_addr
;
447 CamelInternetAddress
*bcc_addr
;
448 CamelInternetAddress
*target
;
449 const gchar
*text_addr
, *header
;
450 gboolean seen_hidden_list
= FALSE
;
453 to_addr
= camel_internet_address_new ();
454 cc_addr
= camel_internet_address_new ();
455 bcc_addr
= camel_internet_address_new ();
457 for (i
= 0; to_destv
!= NULL
&& to_destv
[i
] != NULL
; ++i
) {
458 text_addr
= e_destination_get_address (to_destv
[i
]);
460 if (text_addr
&& *text_addr
) {
462 if (e_destination_is_evolution_list (to_destv
[i
])
463 && !e_destination_list_show_addresses (to_destv
[i
])) {
465 seen_hidden_list
= TRUE
;
468 if (camel_address_decode (CAMEL_ADDRESS (target
), text_addr
) <= 0)
469 camel_internet_address_add (target
, "", text_addr
);
473 for (i
= 0; cc_destv
!= NULL
&& cc_destv
[i
] != NULL
; ++i
) {
474 text_addr
= e_destination_get_address (cc_destv
[i
]);
475 if (text_addr
&& *text_addr
) {
477 if (e_destination_is_evolution_list (cc_destv
[i
])
478 && !e_destination_list_show_addresses (cc_destv
[i
])) {
480 seen_hidden_list
= TRUE
;
483 if (camel_address_decode (CAMEL_ADDRESS (target
), text_addr
) <= 0)
484 camel_internet_address_add (target
, "", text_addr
);
488 for (i
= 0; bcc_destv
!= NULL
&& bcc_destv
[i
] != NULL
; ++i
) {
489 text_addr
= e_destination_get_address (bcc_destv
[i
]);
490 if (text_addr
&& *text_addr
) {
491 if (camel_address_decode (CAMEL_ADDRESS (bcc_addr
), text_addr
) <= 0)
492 camel_internet_address_add (bcc_addr
, "", text_addr
);
497 header
= CAMEL_RECIPIENT_TYPE_RESENT_TO
;
499 header
= CAMEL_RECIPIENT_TYPE_TO
;
501 if (camel_address_length (CAMEL_ADDRESS (to_addr
)) > 0) {
502 camel_mime_message_set_recipients (msg
, header
, to_addr
);
503 } else if (seen_hidden_list
) {
504 camel_medium_set_header (
505 CAMEL_MEDIUM (msg
), header
, "Undisclosed-Recipient:;");
508 header
= redirect
? CAMEL_RECIPIENT_TYPE_RESENT_CC
: CAMEL_RECIPIENT_TYPE_CC
;
509 if (camel_address_length (CAMEL_ADDRESS (cc_addr
)) > 0) {
510 camel_mime_message_set_recipients (msg
, header
, cc_addr
);
513 header
= redirect
? CAMEL_RECIPIENT_TYPE_RESENT_BCC
: CAMEL_RECIPIENT_TYPE_BCC
;
514 if (camel_address_length (CAMEL_ADDRESS (bcc_addr
)) > 0) {
515 camel_mime_message_set_recipients (msg
, header
, bcc_addr
);
518 g_object_unref (to_addr
);
519 g_object_unref (cc_addr
);
520 g_object_unref (bcc_addr
);
524 build_message_headers (EMsgComposer
*composer
,
525 CamelMimeMessage
*message
,
528 EComposerHeaderTable
*table
;
529 EComposerHeader
*header
;
531 const gchar
*subject
;
532 const gchar
*reply_to
;
535 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
536 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
538 table
= e_msg_composer_get_header_table (composer
);
540 uid
= e_composer_header_table_get_identity_uid (table
);
541 source
= e_composer_header_table_ref_source (table
, uid
);
544 subject
= e_composer_header_table_get_subject (table
);
545 camel_mime_message_set_subject (message
, subject
);
547 if (source
!= NULL
) {
549 CamelInternetAddress
*addr
;
550 ESourceMailSubmission
*ms
;
551 EComposerHeader
*composer_header
;
552 const gchar
*extension_name
;
553 const gchar
*header_name
;
554 const gchar
*name
, *address
= NULL
;
555 const gchar
*transport_uid
;
556 const gchar
*sent_folder
;
558 composer_header
= e_composer_header_table_get_header (table
, E_COMPOSER_HEADER_FROM
);
559 if (e_composer_from_header_get_override_visible (E_COMPOSER_FROM_HEADER (composer_header
))) {
560 name
= e_composer_header_table_get_from_name (table
);
561 address
= e_composer_header_table_get_from_address (table
);
563 if (address
&& !*address
)
568 ESourceMailIdentity
*mail_identity
;
570 mail_identity
= e_source_get_extension (source
, E_SOURCE_EXTENSION_MAIL_IDENTITY
);
572 name
= e_source_mail_identity_get_name (mail_identity
);
573 address
= e_source_mail_identity_get_address (mail_identity
);
576 extension_name
= E_SOURCE_EXTENSION_MAIL_SUBMISSION
;
577 ms
= e_source_get_extension (source
, extension_name
);
579 sent_folder
= e_source_mail_submission_get_sent_folder (ms
);
580 transport_uid
= e_source_mail_submission_get_transport_uid (ms
);
582 medium
= CAMEL_MEDIUM (message
);
584 /* From: / Resent-From: */
585 addr
= camel_internet_address_new ();
586 camel_internet_address_add (addr
, name
, address
);
590 value
= camel_address_encode (CAMEL_ADDRESS (addr
));
591 camel_medium_set_header (medium
, "Resent-From", value
);
594 camel_mime_message_set_from (message
, addr
);
596 g_object_unref (addr
);
598 /* X-Evolution-Identity */
599 header_name
= "X-Evolution-Identity";
600 camel_medium_set_header (medium
, header_name
, uid
);
602 /* X-Evolution-Fcc */
603 header_name
= "X-Evolution-Fcc";
604 camel_medium_set_header (medium
, header_name
, sent_folder
);
606 /* X-Evolution-Transport */
607 header_name
= "X-Evolution-Transport";
608 camel_medium_set_header (medium
, header_name
, transport_uid
);
610 g_object_unref (source
);
614 reply_to
= e_composer_header_table_get_reply_to (table
);
615 if (reply_to
!= NULL
&& *reply_to
!= '\0') {
616 CamelInternetAddress
*addr
;
618 addr
= camel_internet_address_new ();
620 if (camel_address_unformat (CAMEL_ADDRESS (addr
), reply_to
) > 0)
621 camel_mime_message_set_reply_to (message
, addr
);
623 g_object_unref (addr
);
627 header
= e_composer_header_table_get_header (
628 table
, E_COMPOSER_HEADER_TO
);
629 if (e_composer_header_get_visible (header
)) {
630 EDestination
**to
, **cc
, **bcc
;
632 to
= e_composer_header_table_get_destinations_to (table
);
633 cc
= e_composer_header_table_get_destinations_cc (table
);
634 bcc
= e_composer_header_table_get_destinations_bcc (table
);
636 set_recipients_from_destv (message
, to
, cc
, bcc
, redirect
);
638 e_destination_freev (to
);
639 e_destination_freev (cc
);
640 e_destination_freev (bcc
);
644 camel_mime_message_set_date (message
, CAMEL_MESSAGE_DATE_CURRENT
, 0);
646 /* X-Evolution-PostTo: */
647 header
= e_composer_header_table_get_header (
648 table
, E_COMPOSER_HEADER_POST_TO
);
649 if (e_composer_header_get_visible (header
)) {
651 const gchar
*name
= "X-Evolution-PostTo";
654 medium
= CAMEL_MEDIUM (message
);
655 camel_medium_remove_header (medium
, name
);
657 list
= e_composer_header_table_get_post_to (table
);
658 for (iter
= list
; iter
!= NULL
; iter
= iter
->next
) {
659 gchar
*folder
= iter
->data
;
660 camel_medium_add_header (medium
, name
, folder
);
667 static CamelCipherHash
668 account_hash_algo_to_camel_hash (const gchar
*hash_algo
)
670 CamelCipherHash res
= CAMEL_CIPHER_HASH_DEFAULT
;
672 if (hash_algo
&& *hash_algo
) {
673 if (g_ascii_strcasecmp (hash_algo
, "sha1") == 0)
674 res
= CAMEL_CIPHER_HASH_SHA1
;
675 else if (g_ascii_strcasecmp (hash_algo
, "sha256") == 0)
676 res
= CAMEL_CIPHER_HASH_SHA256
;
677 else if (g_ascii_strcasecmp (hash_algo
, "sha384") == 0)
678 res
= CAMEL_CIPHER_HASH_SHA384
;
679 else if (g_ascii_strcasecmp (hash_algo
, "sha512") == 0)
680 res
= CAMEL_CIPHER_HASH_SHA512
;
687 composer_add_charset_filter (CamelStream
*stream
,
688 const gchar
*charset
)
690 CamelMimeFilter
*filter
;
692 filter
= camel_mime_filter_charset_new ("UTF-8", charset
);
693 camel_stream_filter_add (CAMEL_STREAM_FILTER (stream
), filter
);
694 g_object_unref (filter
);
698 composer_add_quoted_printable_filter (CamelStream
*stream
)
700 CamelMimeFilter
*filter
;
702 filter
= camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_QP_ENC
);
703 camel_stream_filter_add (CAMEL_STREAM_FILTER (stream
), filter
);
704 g_object_unref (filter
);
706 filter
= camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_FROM
);
707 camel_stream_filter_add (CAMEL_STREAM_FILTER (stream
), filter
);
708 g_object_unref (filter
);
711 /* Helper for composer_build_message_thread() */
713 composer_build_message_pgp (AsyncContext
*context
,
714 GCancellable
*cancellable
,
717 ESourceOpenPGP
*extension
;
718 CamelCipherContext
*cipher
;
719 CamelDataWrapper
*content
;
720 CamelMimePart
*mime_part
;
721 const gchar
*extension_name
;
722 const gchar
*pgp_key_id
;
723 const gchar
*signing_algorithm
;
724 gboolean always_trust
;
725 gboolean encrypt_to_self
;
727 /* Return silently if we're not signing or encrypting with PGP. */
728 if (!context
->pgp_sign
&& !context
->pgp_encrypt
)
731 extension_name
= E_SOURCE_EXTENSION_OPENPGP
;
732 extension
= e_source_get_extension (context
->source
, extension_name
);
734 always_trust
= e_source_openpgp_get_always_trust (extension
);
735 encrypt_to_self
= e_source_openpgp_get_encrypt_to_self (extension
);
736 pgp_key_id
= e_source_openpgp_get_key_id (extension
);
737 signing_algorithm
= e_source_openpgp_get_signing_algorithm (extension
);
739 mime_part
= camel_mime_part_new ();
741 camel_medium_set_content (
742 CAMEL_MEDIUM (mime_part
),
743 context
->top_level_part
);
745 if (context
->top_level_part
== context
->text_plain_part
)
746 camel_mime_part_set_encoding (
747 mime_part
, context
->plain_encoding
);
749 g_object_unref (context
->top_level_part
);
750 context
->top_level_part
= NULL
;
752 if (pgp_key_id
== NULL
|| *pgp_key_id
== '\0')
753 camel_internet_address_get (
754 context
->from
, 0, NULL
, &pgp_key_id
);
756 if (context
->pgp_sign
) {
757 CamelMimePart
*npart
;
760 npart
= camel_mime_part_new ();
762 cipher
= camel_gpg_context_new (context
->session
);
763 camel_gpg_context_set_always_trust (
764 CAMEL_GPG_CONTEXT (cipher
), always_trust
);
766 success
= camel_cipher_context_sign_sync (
768 account_hash_algo_to_camel_hash (signing_algorithm
),
769 mime_part
, npart
, cancellable
, error
);
771 g_object_unref (cipher
);
773 g_object_unref (mime_part
);
776 g_object_unref (npart
);
783 if (context
->pgp_encrypt
) {
784 CamelMimePart
*npart
;
787 npart
= camel_mime_part_new ();
789 /* Check to see if we should encrypt to self.
790 * NB: Gets removed immediately after use. */
791 if (encrypt_to_self
&& pgp_key_id
!= NULL
)
794 g_strdup (pgp_key_id
));
796 cipher
= camel_gpg_context_new (context
->session
);
797 camel_gpg_context_set_always_trust (
798 CAMEL_GPG_CONTEXT (cipher
), always_trust
);
800 success
= camel_cipher_context_encrypt_sync (
801 cipher
, pgp_key_id
, context
->recipients
,
802 mime_part
, npart
, cancellable
, error
);
804 g_object_unref (cipher
);
806 if (encrypt_to_self
&& pgp_key_id
!= NULL
)
807 g_ptr_array_set_size (
809 context
->recipients
->len
- 1);
811 g_object_unref (mime_part
);
814 g_object_unref (npart
);
821 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
822 context
->top_level_part
= g_object_ref (content
);
824 g_object_unref (mime_part
);
831 composer_build_message_smime (AsyncContext
*context
,
832 GCancellable
*cancellable
,
835 ESourceSMIME
*extension
;
836 CamelCipherContext
*cipher
;
837 CamelMimePart
*mime_part
;
838 const gchar
*extension_name
;
839 const gchar
*signing_algorithm
;
840 const gchar
*signing_certificate
;
841 const gchar
*encryption_certificate
;
842 gboolean encrypt_to_self
;
843 gboolean have_signing_certificate
;
844 gboolean have_encryption_certificate
;
846 /* Return silently if we're not signing or encrypting with S/MIME. */
847 if (!context
->smime_sign
&& !context
->smime_encrypt
)
850 extension_name
= E_SOURCE_EXTENSION_SMIME
;
851 extension
= e_source_get_extension (context
->source
, extension_name
);
854 e_source_smime_get_encrypt_to_self (extension
);
857 e_source_smime_get_signing_algorithm (extension
);
859 signing_certificate
=
860 e_source_smime_get_signing_certificate (extension
);
862 encryption_certificate
=
863 e_source_smime_get_encryption_certificate (extension
);
865 have_signing_certificate
=
866 (signing_certificate
!= NULL
) &&
867 (*signing_certificate
!= '\0');
869 have_encryption_certificate
=
870 (encryption_certificate
!= NULL
) &&
871 (*encryption_certificate
!= '\0');
873 if (context
->smime_sign
&& !have_signing_certificate
) {
875 error
, CAMEL_ERROR
, CAMEL_ERROR_GENERIC
,
876 _("Cannot sign outgoing message: "
877 "No signing certificate set for "
882 if (context
->smime_encrypt
&& !have_encryption_certificate
) {
884 error
, CAMEL_ERROR
, CAMEL_ERROR_GENERIC
,
885 _("Cannot encrypt outgoing message: "
886 "No encryption certificate set for "
891 mime_part
= camel_mime_part_new ();
893 camel_medium_set_content (
894 CAMEL_MEDIUM (mime_part
),
895 context
->top_level_part
);
897 if (context
->top_level_part
== context
->text_plain_part
)
898 camel_mime_part_set_encoding (
899 mime_part
, context
->plain_encoding
);
901 g_object_unref (context
->top_level_part
);
902 context
->top_level_part
= NULL
;
904 if (context
->smime_sign
) {
905 CamelMimePart
*npart
;
908 npart
= camel_mime_part_new ();
910 cipher
= camel_smime_context_new (context
->session
);
912 /* if we're also encrypting, envelope-sign rather than clear-sign */
913 if (context
->smime_encrypt
) {
914 camel_smime_context_set_sign_mode (
915 (CamelSMIMEContext
*) cipher
,
916 CAMEL_SMIME_SIGN_ENVELOPED
);
917 camel_smime_context_set_encrypt_key (
918 (CamelSMIMEContext
*) cipher
,
919 TRUE
, encryption_certificate
);
920 } else if (have_encryption_certificate
) {
921 camel_smime_context_set_encrypt_key (
922 (CamelSMIMEContext
*) cipher
,
923 TRUE
, encryption_certificate
);
926 success
= camel_cipher_context_sign_sync (
927 cipher
, signing_certificate
,
928 account_hash_algo_to_camel_hash (signing_algorithm
),
929 mime_part
, npart
, cancellable
, error
);
931 g_object_unref (cipher
);
933 g_object_unref (mime_part
);
936 g_object_unref (npart
);
943 if (context
->smime_encrypt
) {
946 /* Check to see if we should encrypt to self.
947 * NB: Gets removed immediately after use. */
950 context
->recipients
, g_strdup (
951 encryption_certificate
));
953 cipher
= camel_smime_context_new (context
->session
);
954 camel_smime_context_set_encrypt_key (
955 (CamelSMIMEContext
*) cipher
, TRUE
,
956 encryption_certificate
);
958 success
= camel_cipher_context_encrypt_sync (
960 context
->recipients
, mime_part
,
961 CAMEL_MIME_PART (context
->message
),
964 g_object_unref (cipher
);
970 g_ptr_array_set_size (
972 context
->recipients
->len
- 1);
975 /* we replaced the message directly, we don't want to do reparenting foo */
976 if (context
->smime_encrypt
) {
977 context
->skip_content
= TRUE
;
979 CamelDataWrapper
*content
;
981 content
= camel_medium_get_content (
982 CAMEL_MEDIUM (mime_part
));
983 context
->top_level_part
= g_object_ref (content
);
986 g_object_unref (mime_part
);
993 composer_build_message_thread (GSimpleAsyncResult
*simple
,
994 EMsgComposer
*composer
,
995 GCancellable
*cancellable
)
997 AsyncContext
*context
;
998 GError
*error
= NULL
;
1000 context
= g_simple_async_result_get_op_res_gpointer (simple
);
1002 /* Setup working recipient list if we're encrypting. */
1003 if (context
->pgp_encrypt
|| context
->smime_encrypt
) {
1006 const gchar
*types
[] = {
1007 CAMEL_RECIPIENT_TYPE_TO
,
1008 CAMEL_RECIPIENT_TYPE_CC
,
1009 CAMEL_RECIPIENT_TYPE_BCC
1012 context
->recipients
= g_ptr_array_new_with_free_func (
1013 (GDestroyNotify
) g_free
);
1014 for (ii
= 0; ii
< G_N_ELEMENTS (types
); ii
++) {
1015 CamelInternetAddress
*addr
;
1016 const gchar
*address
;
1018 addr
= camel_mime_message_get_recipients (
1019 context
->message
, types
[ii
]);
1020 for (jj
= 0; camel_internet_address_get (addr
, jj
, NULL
, &address
); jj
++)
1022 context
->recipients
,
1023 g_strdup (address
));
1027 if (!composer_build_message_pgp (context
, cancellable
, &error
)) {
1028 g_simple_async_result_take_error (simple
, error
);
1032 #if defined (HAVE_NSS)
1033 if (!composer_build_message_smime (context
, cancellable
, &error
)) {
1034 g_simple_async_result_take_error (simple
, error
);
1037 #endif /* HAVE_NSS */
1041 composer_add_evolution_composer_mode_header (CamelMedium
*medium
,
1042 EMsgComposer
*composer
)
1045 EHTMLEditor
*editor
;
1046 EHTMLEditorView
*view
;
1048 editor
= e_msg_composer_get_editor (composer
);
1049 view
= e_html_editor_get_view (editor
);
1050 html_mode
= e_html_editor_view_get_html_mode (view
);
1052 camel_medium_add_header (
1054 "X-Evolution-Composer-Mode",
1055 html_mode
? "text/html" : "text/plain");
1059 composer_add_evolution_format_header (CamelMedium
*medium
,
1060 ComposerFlags flags
)
1064 string
= g_string_sized_new (128);
1066 if ((flags
& COMPOSER_FLAG_HTML_CONTENT
) || (flags
& COMPOSER_FLAG_SAVE_DRAFT
))
1067 g_string_append (string
, "text/html");
1069 g_string_append (string
, "text/plain");
1071 if (flags
& COMPOSER_FLAG_PGP_SIGN
)
1072 g_string_append (string
, ", pgp-sign");
1074 if (flags
& COMPOSER_FLAG_PGP_ENCRYPT
)
1075 g_string_append (string
, ", pgp-encrypt");
1077 if (flags
& COMPOSER_FLAG_SMIME_SIGN
)
1078 g_string_append (string
, ", smime-sign");
1080 if (flags
& COMPOSER_FLAG_SMIME_ENCRYPT
)
1081 g_string_append (string
, ", smime-encrypt");
1083 camel_medium_add_header (
1084 medium
, "X-Evolution-Format", string
->str
);
1086 g_string_free (string
, TRUE
);
1090 composer_build_message (EMsgComposer
*composer
,
1091 ComposerFlags flags
,
1093 GCancellable
*cancellable
,
1094 GAsyncReadyCallback callback
,
1097 EMsgComposerPrivate
*priv
;
1098 GSimpleAsyncResult
*simple
;
1099 AsyncContext
*context
;
1100 EAttachmentView
*view
;
1101 EAttachmentStore
*store
;
1102 EComposerHeaderTable
*table
;
1103 CamelDataWrapper
*html
;
1104 ESourceMailIdentity
*mi
;
1105 const gchar
*extension_name
;
1106 const gchar
*iconv_charset
= NULL
;
1107 const gchar
*identity_uid
;
1108 const gchar
*organization
;
1109 CamelMultipart
*body
= NULL
;
1110 CamelContentType
*type
;
1111 CamelStream
*stream
;
1112 CamelStream
*mem_stream
;
1113 CamelMimePart
*part
;
1116 gchar
*charset
, *message_uid
;
1117 const gchar
*from_domain
;
1120 priv
= composer
->priv
;
1121 table
= e_msg_composer_get_header_table (composer
);
1122 view
= e_msg_composer_get_attachment_view (composer
);
1123 store
= e_attachment_view_get_store (view
);
1125 identity_uid
= e_composer_header_table_get_identity_uid (table
);
1126 source
= e_composer_header_table_ref_source (table
, identity_uid
);
1127 g_return_if_fail (source
!= NULL
);
1129 /* Do all the non-blocking work here, and defer
1130 * any blocking operations to a separate thread. */
1132 context
= g_slice_new0 (AsyncContext
);
1133 context
->source
= source
; /* takes the reference */
1134 context
->session
= e_msg_composer_ref_session (composer
);
1135 context
->from
= e_msg_composer_get_from (composer
);
1137 if (!(flags
& COMPOSER_FLAG_SAVE_DRAFT
)) {
1138 if (flags
& COMPOSER_FLAG_PGP_SIGN
)
1139 context
->pgp_sign
= TRUE
;
1141 if (flags
& COMPOSER_FLAG_PGP_ENCRYPT
)
1142 context
->pgp_encrypt
= TRUE
;
1144 if (flags
& COMPOSER_FLAG_SMIME_SIGN
)
1145 context
->smime_sign
= TRUE
;
1147 if (flags
& COMPOSER_FLAG_SMIME_ENCRYPT
)
1148 context
->smime_encrypt
= TRUE
;
1151 context
->need_thread
=
1152 context
->pgp_sign
|| context
->pgp_encrypt
||
1153 context
->smime_sign
|| context
->smime_encrypt
;
1155 simple
= g_simple_async_result_new (
1156 G_OBJECT (composer
), callback
,
1157 user_data
, composer_build_message
);
1159 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
1161 g_simple_async_result_set_op_res_gpointer (
1162 simple
, context
, (GDestroyNotify
) async_context_free
);
1164 /* If this is a redirected message, just tweak the headers. */
1165 if (priv
->redirect
) {
1166 context
->skip_content
= TRUE
;
1167 context
->message
= g_object_ref (priv
->redirect
);
1168 build_message_headers (composer
, context
->message
, TRUE
);
1169 g_simple_async_result_complete (simple
);
1170 g_object_unref (simple
);
1174 context
->message
= camel_mime_message_new ();
1176 if (context
->from
&& camel_internet_address_get (context
->from
, 0, NULL
, &from_domain
)) {
1177 const gchar
*at
= strchr (from_domain
, '@');
1179 from_domain
= at
+ 1;
1186 if (!from_domain
|| !*from_domain
)
1187 from_domain
= "localhost";
1189 message_uid
= camel_header_msgid_generate (from_domain
);
1191 /* Explicitly generate a Message-ID header here so it's
1192 * consistent for all outbound streams (SMTP, Fcc, etc). */
1193 camel_mime_message_set_message_id (context
->message
, message_uid
);
1194 g_free (message_uid
);
1196 build_message_headers (composer
, context
->message
, FALSE
);
1197 for (i
= 0; i
< priv
->extra_hdr_names
->len
; i
++) {
1198 camel_medium_add_header (
1199 CAMEL_MEDIUM (context
->message
),
1200 priv
->extra_hdr_names
->pdata
[i
],
1201 priv
->extra_hdr_values
->pdata
[i
]);
1204 extension_name
= E_SOURCE_EXTENSION_MAIL_IDENTITY
;
1205 mi
= e_source_get_extension (source
, extension_name
);
1206 organization
= e_source_mail_identity_get_organization (mi
);
1208 /* Disposition-Notification-To */
1209 if (flags
& COMPOSER_FLAG_REQUEST_READ_RECEIPT
) {
1210 const gchar
*mdn_address
;
1212 mdn_address
= e_source_mail_identity_get_reply_to (mi
);
1213 if (mdn_address
== NULL
)
1214 mdn_address
= e_source_mail_identity_get_address (mi
);
1215 if (mdn_address
!= NULL
)
1216 camel_medium_add_header (
1217 CAMEL_MEDIUM (context
->message
),
1218 "Disposition-Notification-To", mdn_address
);
1222 if (flags
& COMPOSER_FLAG_PRIORITIZE_MESSAGE
)
1223 camel_medium_add_header (
1224 CAMEL_MEDIUM (context
->message
),
1228 if (organization
!= NULL
&& *organization
!= '\0') {
1229 gchar
*encoded_organization
;
1231 encoded_organization
= camel_header_encode_string (
1232 (const guchar
*) organization
);
1233 camel_medium_set_header (
1234 CAMEL_MEDIUM (context
->message
),
1235 "Organization", encoded_organization
);
1236 g_free (encoded_organization
);
1239 if (flags
& COMPOSER_FLAG_SAVE_DRAFT
) {
1241 EHTMLEditor
*editor
;
1242 EHTMLEditorView
*view
;
1243 EHTMLEditorSelection
*selection
;
1245 editor
= e_msg_composer_get_editor (composer
);
1246 view
= e_html_editor_get_view (editor
);
1247 selection
= e_html_editor_view_get_selection (view
);
1249 /* X-Evolution-Format */
1250 composer_add_evolution_format_header (
1251 CAMEL_MEDIUM (context
->message
), flags
);
1253 /* X-Evolution-Composer-Mode */
1254 composer_add_evolution_composer_mode_header (
1255 CAMEL_MEDIUM (context
->message
), composer
);
1257 data
= g_byte_array_new ();
1259 e_html_editor_view_embed_styles (view
);
1260 e_html_editor_selection_save (selection
);
1262 text
= e_html_editor_view_get_text_html_for_drafts (view
);
1264 e_html_editor_view_remove_embed_styles (view
);
1265 e_html_editor_selection_restore (selection
);
1266 e_html_editor_view_force_spell_check_in_viewport (view
);
1268 g_byte_array_append (data
, (guint8
*) text
, strlen (text
));
1272 type
= camel_content_type_new ("text", "html");
1273 camel_content_type_set_param (type
, "charset", "utf-8");
1274 iconv_charset
= camel_iconv_charset_name ("utf-8");
1276 goto wrap_drafts_html
;
1279 /* Build the text/plain part. */
1281 if (priv
->mime_body
) {
1282 if (text_requires_quoted_printable (priv
->mime_body
, -1)) {
1283 context
->plain_encoding
=
1284 CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
;
1286 context
->plain_encoding
= CAMEL_TRANSFER_ENCODING_7BIT
;
1287 for (i
= 0; priv
->mime_body
[i
]; i
++) {
1288 if ((guchar
) priv
->mime_body
[i
] > 127) {
1289 context
->plain_encoding
=
1290 CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
;
1296 data
= g_byte_array_new ();
1297 g_byte_array_append (
1298 data
, (const guint8
*) priv
->mime_body
,
1299 strlen (priv
->mime_body
));
1300 type
= camel_content_type_decode (priv
->mime_type
);
1304 EHTMLEditor
*editor
;
1305 EHTMLEditorView
*view
;
1307 editor
= e_msg_composer_get_editor (composer
);
1308 view
= e_html_editor_get_view (editor
);
1309 data
= g_byte_array_new ();
1310 text
= e_html_editor_view_get_text_plain (view
);
1311 g_byte_array_append (data
, (guint8
*) text
, strlen (text
));
1314 type
= camel_content_type_new ("text", "plain");
1315 charset
= best_charset (
1316 data
, priv
->charset
, &context
->plain_encoding
);
1317 if (charset
!= NULL
) {
1318 camel_content_type_set_param (type
, "charset", charset
);
1319 iconv_charset
= camel_iconv_charset_name (charset
);
1325 mem_stream
= camel_stream_mem_new_with_byte_array (data
);
1326 stream
= camel_stream_filter_new (mem_stream
);
1327 g_object_unref (mem_stream
);
1329 /* Convert the stream to the appropriate charset. */
1330 if (iconv_charset
&& g_ascii_strcasecmp (iconv_charset
, "UTF-8") != 0)
1331 composer_add_charset_filter (stream
, iconv_charset
);
1333 /* Encode the stream to quoted-printable if necessary. */
1334 if (context
->plain_encoding
== CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
)
1335 composer_add_quoted_printable_filter (stream
);
1337 /* Construct the content object. This does not block since
1338 * we're constructing the data wrapper from a memory stream. */
1339 context
->top_level_part
= camel_data_wrapper_new ();
1340 camel_data_wrapper_construct_from_stream_sync (
1341 context
->top_level_part
, stream
, NULL
, NULL
);
1342 g_object_unref (stream
);
1344 context
->text_plain_part
= g_object_ref (context
->top_level_part
);
1346 /* Avoid re-encoding the data when adding it to a MIME part. */
1347 if (context
->plain_encoding
== CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
)
1348 context
->top_level_part
->encoding
= context
->plain_encoding
;
1350 camel_data_wrapper_set_mime_type_field (
1351 context
->top_level_part
, type
);
1353 camel_content_type_unref (type
);
1355 /* Build the text/html part, and wrap it and the text/plain part
1356 * in a multipart/alternative part. Additionally, if there are
1357 * inline images then wrap the multipart/alternative part along
1358 * with the images in a multipart/related part.
1360 * So the structure of all this will be:
1363 * multipart/alternative
1366 * image/<<whatever>>
1367 * image/<<whatever>>
1371 if ((flags
& COMPOSER_FLAG_HTML_CONTENT
) != 0 &&
1372 !(flags
& COMPOSER_FLAG_SAVE_DRAFT
)) {
1376 gboolean pre_encode
;
1377 EHTMLEditor
*editor
;
1378 EHTMLEditorView
*view
;
1379 GList
*inline_images
= NULL
;
1381 editor
= e_msg_composer_get_editor (composer
);
1382 view
= e_html_editor_get_view (editor
);
1384 data
= g_byte_array_new ();
1385 text
= e_html_editor_view_get_text_html (view
, from_domain
, &inline_images
);
1386 length
= strlen (text
);
1387 g_byte_array_append (data
, (guint8
*) text
, (guint
) length
);
1388 pre_encode
= text_requires_quoted_printable (text
, length
);
1391 mem_stream
= camel_stream_mem_new_with_byte_array (data
);
1392 stream
= camel_stream_filter_new (mem_stream
);
1393 g_object_unref (mem_stream
);
1396 composer_add_quoted_printable_filter (stream
);
1398 /* Construct the content object. This does not block since
1399 * we're constructing the data wrapper from a memory stream. */
1400 html
= camel_data_wrapper_new ();
1401 camel_data_wrapper_construct_from_stream_sync (
1402 html
, stream
, NULL
, NULL
);
1403 g_object_unref (stream
);
1405 camel_data_wrapper_set_mime_type (
1406 html
, "text/html; charset=utf-8");
1408 /* Avoid re-encoding the data when adding it to a MIME part. */
1411 CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
;
1413 /* Build the multipart/alternative */
1414 body
= camel_multipart_new ();
1415 camel_data_wrapper_set_mime_type (
1416 CAMEL_DATA_WRAPPER (body
), "multipart/alternative");
1417 camel_multipart_set_boundary (body
, NULL
);
1419 /* Add the text/plain part. */
1420 part
= camel_mime_part_new ();
1421 camel_medium_set_content (
1422 CAMEL_MEDIUM (part
), context
->top_level_part
);
1423 camel_mime_part_set_encoding (part
, context
->plain_encoding
);
1424 camel_multipart_add_part (body
, part
);
1425 g_object_unref (part
);
1427 /* Add the text/html part. */
1428 part
= camel_mime_part_new ();
1429 camel_medium_set_content (CAMEL_MEDIUM (part
), html
);
1430 camel_mime_part_set_encoding (
1431 part
, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
);
1432 camel_multipart_add_part (body
, part
);
1433 g_object_unref (part
);
1435 g_object_unref (context
->top_level_part
);
1436 g_object_unref (html
);
1438 /* If there are inlined images, construct a multipart/related
1439 * containing the multipart/alternative and the images. */
1440 count
= g_list_length (inline_images
);
1443 CamelMultipart
*html_with_images
;
1445 html_with_images
= camel_multipart_new ();
1446 camel_data_wrapper_set_mime_type (
1447 CAMEL_DATA_WRAPPER (html_with_images
),
1448 "multipart/related; "
1449 "type=\"multipart/alternative\"");
1450 camel_multipart_set_boundary (html_with_images
, NULL
);
1452 part
= camel_mime_part_new ();
1453 camel_medium_set_content (
1454 CAMEL_MEDIUM (part
),
1455 CAMEL_DATA_WRAPPER (body
));
1456 camel_multipart_add_part (html_with_images
, part
);
1457 g_object_unref (part
);
1459 g_object_unref (body
);
1461 for (ii
= 0; ii
< count
; ii
++) {
1462 CamelMimePart
*part
= g_list_nth_data (inline_images
, ii
);
1463 camel_multipart_add_part (
1464 html_with_images
, part
);
1467 context
->top_level_part
=
1468 CAMEL_DATA_WRAPPER (html_with_images
);
1470 context
->top_level_part
=
1471 CAMEL_DATA_WRAPPER (body
);
1473 g_list_free_full (inline_images
, g_object_unref
);
1476 /* If there are attachments, wrap what we've built so far
1477 * along with the attachments in a multipart/mixed part. */
1478 if (e_attachment_store_get_num_attachments (store
) > 0) {
1479 CamelMultipart
*multipart
= camel_multipart_new ();
1481 /* Generate a random boundary. */
1482 camel_multipart_set_boundary (multipart
, NULL
);
1484 part
= camel_mime_part_new ();
1485 camel_medium_set_content (
1486 CAMEL_MEDIUM (part
),
1487 context
->top_level_part
);
1488 if (context
->top_level_part
== context
->text_plain_part
)
1489 camel_mime_part_set_encoding (
1490 part
, context
->plain_encoding
);
1491 camel_multipart_add_part (multipart
, part
);
1492 g_object_unref (part
);
1494 e_attachment_store_add_to_multipart (
1495 store
, multipart
, priv
->charset
);
1497 g_object_unref (context
->top_level_part
);
1498 context
->top_level_part
= CAMEL_DATA_WRAPPER (multipart
);
1501 /* Run any blocking operations in a separate thread. */
1502 if (context
->need_thread
)
1503 g_simple_async_result_run_in_thread (
1504 simple
, (GSimpleAsyncThreadFunc
)
1505 composer_build_message_thread
,
1506 io_priority
, cancellable
);
1508 g_simple_async_result_complete (simple
);
1510 g_object_unref (simple
);
1513 static CamelMimeMessage
*
1514 composer_build_message_finish (EMsgComposer
*composer
,
1515 GAsyncResult
*result
,
1518 GSimpleAsyncResult
*simple
;
1519 AsyncContext
*context
;
1521 g_return_val_if_fail (
1522 g_simple_async_result_is_valid (
1523 result
, G_OBJECT (composer
), composer_build_message
), NULL
);
1525 simple
= G_SIMPLE_ASYNC_RESULT (result
);
1526 context
= g_simple_async_result_get_op_res_gpointer (simple
);
1528 if (g_simple_async_result_propagate_error (simple
, error
))
1531 /* Finalize some details before returning. */
1533 if (!context
->skip_content
)
1534 camel_medium_set_content (
1535 CAMEL_MEDIUM (context
->message
),
1536 context
->top_level_part
);
1538 if (context
->top_level_part
== context
->text_plain_part
)
1539 camel_mime_part_set_encoding (
1540 CAMEL_MIME_PART (context
->message
),
1541 context
->plain_encoding
);
1543 return g_object_ref (context
->message
);
1549 set_editor_text (EMsgComposer
*composer
,
1552 gboolean set_signature
)
1554 EHTMLEditor
*editor
;
1555 EHTMLEditorView
*view
;
1557 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
1558 g_return_if_fail (text
!= NULL
);
1560 editor
= e_msg_composer_get_editor (composer
);
1561 view
= e_html_editor_get_view (editor
);
1564 e_html_editor_view_set_text_html (view
, text
);
1566 e_html_editor_view_set_text_plain (view
, text
);
1569 e_composer_update_signature (composer
);
1572 /* Miscellaneous callbacks. */
1575 attachment_store_changed_cb (EMsgComposer
*composer
)
1577 EHTMLEditor
*editor
;
1578 EHTMLEditorView
*view
;
1580 /* Mark the editor as changed so it prompts about unsaved
1581 * changes on close. */
1582 editor
= e_msg_composer_get_editor (composer
);
1584 view
= e_html_editor_get_view (editor
);
1585 e_html_editor_view_set_changed (view
, TRUE
);
1590 msg_composer_subject_changed_cb (EMsgComposer
*composer
)
1592 EComposerHeaderTable
*table
;
1593 const gchar
*subject
;
1595 table
= e_msg_composer_get_header_table (composer
);
1596 subject
= e_composer_header_table_get_subject (table
);
1598 if (subject
== NULL
|| *subject
== '\0')
1599 subject
= _("Compose Message");
1601 gtk_window_set_title (GTK_WINDOW (composer
), subject
);
1605 msg_composer_mail_identity_changed_cb (EMsgComposer
*composer
)
1607 EHTMLEditor
*editor
;
1608 EHTMLEditorView
*view
;
1609 EMailSignatureComboBox
*combo_box
;
1610 ESourceMailComposition
*mc
;
1611 ESourceOpenPGP
*pgp
;
1612 ESourceSMIME
*smime
;
1613 EComposerHeaderTable
*table
;
1614 GtkToggleAction
*action
;
1619 gboolean pgp_encrypt
;
1620 gboolean smime_sign
;
1621 gboolean smime_encrypt
;
1622 gboolean is_message_from_edit_as_new
;
1623 const gchar
*extension_name
;
1626 table
= e_msg_composer_get_header_table (composer
);
1627 uid
= e_composer_header_table_get_identity_uid (table
);
1629 /* Silently return if no identity is selected. */
1633 source
= e_composer_header_table_ref_source (table
, uid
);
1634 g_return_if_fail (source
!= NULL
);
1636 extension_name
= E_SOURCE_EXTENSION_MAIL_COMPOSITION
;
1637 mc
= e_source_get_extension (source
, extension_name
);
1639 extension_name
= E_SOURCE_EXTENSION_OPENPGP
;
1640 pgp
= e_source_get_extension (source
, extension_name
);
1641 pgp_sign
= e_source_openpgp_get_sign_by_default (pgp
);
1642 pgp_encrypt
= e_source_openpgp_get_encrypt_by_default (pgp
);
1644 extension_name
= E_SOURCE_EXTENSION_SMIME
;
1645 smime
= e_source_get_extension (source
, extension_name
);
1646 smime_sign
= e_source_smime_get_sign_by_default (smime
);
1647 smime_encrypt
= e_source_smime_get_encrypt_by_default (smime
);
1650 (composer
->priv
->mime_type
== NULL
) ||
1651 e_source_mail_composition_get_sign_imip (mc
) ||
1652 (g_ascii_strncasecmp (
1653 composer
->priv
->mime_type
,
1654 "text/calendar", 13) != 0);
1656 editor
= e_msg_composer_get_editor (composer
);
1657 view
= e_html_editor_get_view (editor
);
1658 is_message_from_edit_as_new
=
1659 e_html_editor_view_is_message_from_edit_as_new (view
);
1661 action
= GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
));
1662 active
= gtk_toggle_action_get_active (action
);
1663 active
&= is_message_from_edit_as_new
;
1664 active
|= (can_sign
&& pgp_sign
);
1665 gtk_toggle_action_set_active (action
, active
);
1667 action
= GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
));
1668 active
= gtk_toggle_action_get_active (action
);
1669 active
&= is_message_from_edit_as_new
;
1670 active
|= pgp_encrypt
;
1671 gtk_toggle_action_set_active (action
, active
);
1673 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
));
1674 active
= gtk_toggle_action_get_active (action
);
1675 active
&= is_message_from_edit_as_new
;
1676 active
|= (can_sign
&& smime_sign
);
1677 gtk_toggle_action_set_active (action
, active
);
1679 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
));
1680 active
= gtk_toggle_action_get_active (action
);
1681 active
&= is_message_from_edit_as_new
;
1682 active
|= smime_encrypt
;
1683 gtk_toggle_action_set_active (action
, active
);
1685 combo_box
= e_composer_header_table_get_signature_combo_box (table
);
1686 e_mail_signature_combo_box_set_identity_uid (combo_box
, uid
);
1688 g_object_unref (source
);
1692 msg_composer_paste_clipboard_targets_cb (GtkClipboard
*clipboard
,
1695 EMsgComposer
*composer
)
1697 if (targets
== NULL
|| n_targets
< 0)
1700 /* Order is important here to ensure common use cases are
1701 * handled correctly. See GNOME bug #603715 for details. */
1703 if (gtk_targets_include_uri (targets
, n_targets
)) {
1704 e_composer_paste_uris (composer
, clipboard
);
1708 if (e_targets_include_html (targets
, n_targets
)) {
1709 e_composer_paste_html (composer
, clipboard
);
1713 if (gtk_targets_include_text (targets
, n_targets
)) {
1714 e_composer_paste_text (composer
, clipboard
);
1718 if (gtk_targets_include_image (targets
, n_targets
, TRUE
)) {
1719 e_composer_paste_image (composer
, clipboard
);
1725 msg_composer_paste_primary_clipboard_cb (EHTMLEditorView
*view
,
1726 EMsgComposer
*composer
)
1728 GtkClipboard
*clipboard
;
1730 clipboard
= gtk_clipboard_get (GDK_SELECTION_PRIMARY
);
1732 gtk_clipboard_request_targets (
1733 clipboard
, (GtkClipboardTargetsReceivedFunc
)
1734 msg_composer_paste_clipboard_targets_cb
, composer
);
1736 g_signal_stop_emission_by_name (view
, "paste-primary-clipboard");
1740 msg_composer_paste_clipboard_cb (EHTMLEditorView
*view
,
1741 EMsgComposer
*composer
)
1743 GtkClipboard
*clipboard
;
1745 clipboard
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
);
1747 gtk_clipboard_request_targets (
1748 clipboard
, (GtkClipboardTargetsReceivedFunc
)
1749 msg_composer_paste_clipboard_targets_cb
, composer
);
1751 g_signal_stop_emission_by_name (view
, "paste-clipboard");
1755 msg_composer_drag_motion_cb (GtkWidget
*widget
,
1756 GdkDragContext
*context
,
1760 EMsgComposer
*composer
)
1762 GtkWidget
*source_widget
;
1763 EHTMLEditor
*editor
= e_msg_composer_get_editor (composer
);
1764 EHTMLEditorView
*editor_view
= e_html_editor_get_view (editor
);
1766 source_widget
= gtk_drag_get_source_widget (context
);
1767 /* When we are doind DnD just inside the web view, the DnD is supposed
1768 * to move things around. */
1769 if (E_IS_HTML_EDITOR_VIEW (source_widget
)) {
1770 if ((gpointer
) editor_view
== (gpointer
) source_widget
) {
1771 gdk_drag_status (context
, GDK_ACTION_MOVE
, time
);
1777 gdk_drag_status (context
, GDK_ACTION_COPY
, time
);
1783 msg_composer_drag_drop_cb (GtkWidget
*widget
,
1784 GdkDragContext
*context
,
1788 EMsgComposer
*composer
)
1791 GtkWidget
*source_widget
;
1793 /* When we are doind DnD just inside the web view, the DnD is supposed
1794 * to move things around. */
1795 source_widget
= gtk_drag_get_source_widget (context
);
1796 if (E_IS_HTML_EDITOR_VIEW (source_widget
)) {
1797 EHTMLEditor
*editor
= e_msg_composer_get_editor (composer
);
1798 EHTMLEditorView
*editor_view
= e_html_editor_get_view (editor
);
1800 if ((gpointer
) editor_view
== (gpointer
) source_widget
)
1804 target
= gtk_drag_dest_find_target (widget
, context
, NULL
);
1805 if (target
== GDK_NONE
)
1806 gdk_drag_status (context
, 0, time
);
1808 /* Prevent WebKit from pasting the URI of file into the view. */
1809 if (composer
->priv
->dnd_is_uri
)
1810 g_signal_stop_emission_by_name (widget
, "drag-drop");
1812 composer
->priv
->dnd_is_uri
= FALSE
;
1814 gdk_drag_status (context
, GDK_ACTION_COPY
, time
);
1815 composer
->priv
->drop_occured
= TRUE
;
1816 gtk_drag_get_data (widget
, context
, target
, time
);
1823 msg_composer_drag_data_received_after_cb (GtkWidget
*widget
,
1824 GdkDragContext
*context
,
1827 GtkSelectionData
*selection
,
1830 EMsgComposer
*composer
)
1832 EHTMLEditor
*editor
;
1833 EHTMLEditorView
*view
;
1834 WebKitDOMDocument
*document
;
1835 WebKitDOMDOMWindow
*dom_window
;
1836 WebKitDOMDOMSelection
*dom_selection
;
1838 if (!composer
->priv
->drop_occured
)
1841 composer
->priv
->drop_occured
= FALSE
;
1843 editor
= e_msg_composer_get_editor (composer
);
1844 view
= e_html_editor_get_view (editor
);
1846 document
= webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view
));
1847 dom_window
= webkit_dom_document_get_default_view (document
);
1848 dom_selection
= webkit_dom_dom_window_get_selection (dom_window
);
1850 /* When text is DnD'ed into the view, WebKit will select it. So let's
1851 * collapse it to its end to have the caret after the DnD'ed text. */
1852 webkit_dom_dom_selection_collapse_to_end (dom_selection
, NULL
);
1854 e_html_editor_view_check_magic_links (view
, FALSE
);
1855 /* Also force spell check on view. */
1856 e_html_editor_view_force_spell_check_in_viewport (view
);
1860 next_uri (guchar
**uri_list
,
1864 guchar
*uri
, *begin
;
1868 while (**uri_list
&& **uri_list
!= '\n' && **uri_list
!= '\r' && *list_len
) {
1874 uri
= (guchar
*) g_strndup ((gchar
*) begin
, *len
);
1876 while ((!**uri_list
|| **uri_list
== '\n' || **uri_list
== '\r') && *list_len
) {
1881 return (gchar
*) uri
;
1885 msg_composer_drag_data_received_cb (GtkWidget
*widget
,
1886 GdkDragContext
*context
,
1889 GtkSelectionData
*selection
,
1892 EMsgComposer
*composer
)
1894 EHTMLEditor
*editor
;
1895 EHTMLEditorView
*html_editor_view
;
1896 EHTMLEditorSelection
*editor_selection
;
1897 gboolean html_mode
, same_widget
= FALSE
;
1898 GtkWidget
*source_widget
;
1900 editor
= e_msg_composer_get_editor (composer
);
1901 html_editor_view
= e_html_editor_get_view (editor
);
1902 html_mode
= e_html_editor_view_get_html_mode (html_editor_view
);
1903 editor_selection
= e_html_editor_view_get_selection (html_editor_view
);
1905 /* When we are doind DnD just inside the web view, the DnD is supposed
1906 * to move things around. */
1907 source_widget
= gtk_drag_get_source_widget (context
);
1908 if (E_IS_HTML_EDITOR_VIEW (source_widget
)) {
1909 EHTMLEditor
*editor
= e_msg_composer_get_editor (composer
);
1910 EHTMLEditorView
*editor_view
= e_html_editor_get_view (editor
);
1912 if ((gpointer
) editor_view
== (gpointer
) source_widget
)
1916 /* Leave DnD inside the view on WebKit. */
1917 if (composer
->priv
->drop_occured
&& same_widget
) {
1918 gdk_drag_status (context
, 0, time
);
1922 if (!composer
->priv
->drop_occured
) {
1924 /* Check if we are DnD'ing some URI, if so WebKit will
1925 * insert the URI into the view and we have to prevent it
1926 * from doing that. */
1927 if (info
== DND_TARGET_TYPE_TEXT_URI_LIST
) {
1930 uris
= gtk_selection_data_get_uris (selection
);
1931 composer
->priv
->dnd_is_uri
= uris
!= NULL
;
1938 composer
->priv
->dnd_is_uri
= FALSE
;
1940 /* Leave the text on WebKit to handle it. */
1941 if (info
== DND_TARGET_TYPE_UTF8_STRING
||
1942 info
== DND_TARGET_TYPE_STRING
||
1943 info
== DND_TARGET_TYPE_TEXT_PLAIN
||
1944 info
== DND_TARGET_TYPE_TEXT_PLAIN_UTF8
) {
1945 gdk_drag_status (context
, 0, time
);
1949 if (info
== DND_TARGET_TYPE_TEXT_HTML
) {
1955 data
= gtk_selection_data_get_data (selection
);
1956 length
= gtk_selection_data_get_length (selection
);
1958 if (!data
|| length
< 0) {
1959 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
1963 e_html_editor_selection_set_on_point (editor_selection
, x
, y
);
1967 text
= next_uri ((guchar
**) &data
, &len
, &list_len
);
1968 e_html_editor_selection_insert_html (editor_selection
, text
);
1972 e_html_editor_view_check_magic_links (html_editor_view
, FALSE
);
1973 e_html_editor_view_force_spell_check_in_viewport (html_editor_view
);
1975 e_html_editor_selection_scroll_to_caret (editor_selection
);
1977 gtk_drag_finish (context
, TRUE
, FALSE
, time
);
1981 /* HTML mode has a few special cases for drops... */
1982 /* If we're receiving URIs and -all- the URIs point to
1983 * image files, we want the image(s) to be inserted in
1984 * the message body. */
1985 if (html_mode
&& e_composer_selection_is_image_uris (composer
, selection
)) {
1991 data
= gtk_selection_data_get_data (selection
);
1992 length
= gtk_selection_data_get_length (selection
);
1994 if (!data
|| length
< 0) {
1995 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
1999 e_html_editor_selection_set_on_point (editor_selection
, x
, y
);
2003 uri
= next_uri ((guchar
**) &data
, &len
, &list_len
);
2004 e_html_editor_selection_insert_image (editor_selection
, uri
);
2008 gtk_drag_finish (context
, TRUE
, FALSE
, time
);
2009 } else if (html_mode
&& e_composer_selection_is_base64_uris (composer
, selection
)) {
2015 data
= gtk_selection_data_get_data (selection
);
2016 length
= gtk_selection_data_get_length (selection
);
2018 if (!data
|| length
< 0) {
2019 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
2023 e_html_editor_selection_set_on_point (editor_selection
, x
, y
);
2027 uri
= next_uri ((guchar
**) &data
, &len
, &list_len
);
2028 e_html_editor_selection_insert_image (editor_selection
, uri
);
2032 gtk_drag_finish (context
, TRUE
, FALSE
, time
);
2034 EAttachmentView
*view
= e_msg_composer_get_attachment_view (composer
);
2036 /* Forward the data to the attachment view. Note that calling
2037 * e_attachment_view_drag_data_received() will not work because
2038 * that function only handles the case where all the other drag
2039 * handlers have failed. */
2040 e_attachment_paned_drag_data_received (
2041 E_ATTACHMENT_PANED (view
),
2042 context
, x
, y
, selection
, info
, time
);
2047 msg_composer_notify_header_cb (EMsgComposer
*composer
)
2049 EHTMLEditor
*editor
;
2050 EHTMLEditorView
*view
;
2052 editor
= e_msg_composer_get_editor (composer
);
2053 view
= e_html_editor_get_view (editor
);
2054 e_html_editor_view_set_changed (view
, TRUE
);
2058 msg_composer_delete_event_cb (EMsgComposer
*composer
)
2061 GtkApplication
*application
;
2064 shell
= e_msg_composer_get_shell (composer
);
2066 /* If the "async" action group is insensitive, it means an
2067 * asynchronous operation is in progress. Block the event. */
2068 if (!gtk_action_group_get_sensitive (composer
->priv
->async_actions
))
2071 application
= GTK_APPLICATION (shell
);
2072 windows
= gtk_application_get_windows (application
);
2074 if (g_list_length (windows
) == 1) {
2075 /* This is the last watched window, use the quit
2076 * mechanism to have a draft saved properly */
2077 e_shell_quit (shell
, E_SHELL_QUIT_ACTION
);
2079 /* There are more watched windows opened,
2080 * invoke only a close action */
2081 gtk_action_activate (ACTION (CLOSE
));
2088 msg_composer_realize_cb (EMsgComposer
*composer
)
2090 GSettings
*settings
;
2093 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2095 action
= ACTION (TOOLBAR_PGP_SIGN
);
2096 if (gtk_action_get_visible (action
) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
2097 gtk_action_set_visible (action
, FALSE
);
2099 action
= ACTION (TOOLBAR_PGP_ENCRYPT
);
2100 if (gtk_action_get_visible (action
) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
2101 gtk_action_set_visible (action
, FALSE
);
2103 action
= ACTION (TOOLBAR_SMIME_SIGN
);
2104 if (gtk_action_get_visible (action
) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
2105 gtk_action_set_visible (action
, FALSE
);
2107 action
= ACTION (TOOLBAR_SMIME_ENCRYPT
);
2108 if (gtk_action_get_visible (action
) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
2109 gtk_action_set_visible (action
, FALSE
);
2111 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
2113 if (g_settings_get_boolean (settings
, "composer-toolbar-show-sign-encrypt")) {
2114 EComposerHeaderTable
*table
;
2116 const gchar
*identity_uid
;
2118 table
= e_msg_composer_get_header_table (composer
);
2119 identity_uid
= e_composer_header_table_get_identity_uid (table
);
2120 source
= e_composer_header_table_ref_source (table
, identity_uid
);
2123 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_OPENPGP
)) {
2126 key_id
= e_source_openpgp_dup_key_id (e_source_get_extension (source
, E_SOURCE_EXTENSION_OPENPGP
));
2128 if (key_id
&& *key_id
) {
2129 action
= ACTION (TOOLBAR_PGP_SIGN
);
2130 gtk_action_set_visible (action
, TRUE
);
2132 action
= ACTION (TOOLBAR_PGP_ENCRYPT
);
2133 gtk_action_set_visible (action
, TRUE
);
2139 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_SMIME
)) {
2140 ESourceSMIME
*smime_extension
;
2143 smime_extension
= e_source_get_extension (source
, E_SOURCE_EXTENSION_SMIME
);
2145 certificate
= e_source_smime_dup_signing_certificate (smime_extension
);
2146 if (certificate
&& *certificate
)
2147 gtk_action_set_visible (ACTION (TOOLBAR_SMIME_SIGN
), TRUE
);
2148 g_free (certificate
);
2150 certificate
= e_source_smime_dup_encryption_certificate (smime_extension
);
2151 if (certificate
&& *certificate
)
2152 gtk_action_set_visible (ACTION (TOOLBAR_SMIME_ENCRYPT
), TRUE
);
2153 g_free (certificate
);
2156 g_clear_object (&source
);
2160 g_clear_object (&settings
);
2164 msg_composer_prepare_for_quit_cb (EShell
*shell
,
2165 EActivity
*activity
,
2166 EMsgComposer
*composer
)
2168 if (e_msg_composer_is_exiting (composer
)) {
2169 /* needs save draft first */
2170 g_object_ref (activity
);
2172 G_OBJECT (composer
), (GWeakNotify
)
2173 g_object_unref
, activity
);
2174 gtk_action_activate (ACTION (SAVE_DRAFT
));
2179 msg_composer_quit_requested_cb (EShell
*shell
,
2180 EShellQuitReason reason
,
2181 EMsgComposer
*composer
)
2183 if (e_msg_composer_is_exiting (composer
)) {
2184 g_signal_handlers_disconnect_by_func (
2185 shell
, msg_composer_quit_requested_cb
, composer
);
2186 g_signal_handlers_disconnect_by_func (
2187 shell
, msg_composer_prepare_for_quit_cb
, composer
);
2188 } else if (!e_msg_composer_can_close (composer
, FALSE
) &&
2189 !e_msg_composer_is_exiting (composer
)) {
2190 e_shell_cancel_quit (shell
);
2195 msg_composer_set_shell (EMsgComposer
*composer
,
2198 g_return_if_fail (E_IS_SHELL (shell
));
2199 g_return_if_fail (composer
->priv
->shell
== NULL
);
2201 composer
->priv
->shell
= shell
;
2203 g_object_add_weak_pointer (
2204 G_OBJECT (shell
), &composer
->priv
->shell
);
2208 msg_composer_set_property (GObject
*object
,
2210 const GValue
*value
,
2213 switch (property_id
) {
2215 msg_composer_set_shell (
2216 E_MSG_COMPOSER (object
),
2217 g_value_get_object (value
));
2221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2225 msg_composer_get_property (GObject
*object
,
2230 switch (property_id
) {
2232 g_value_set_boolean (
2233 value
, e_msg_composer_is_busy (
2234 E_MSG_COMPOSER (object
)));
2237 case PROP_FOCUS_TRACKER
:
2238 g_value_set_object (
2239 value
, e_msg_composer_get_focus_tracker (
2240 E_MSG_COMPOSER (object
)));
2244 g_value_set_object (
2245 value
, e_msg_composer_get_shell (
2246 E_MSG_COMPOSER (object
)));
2250 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2254 msg_composer_finalize (GObject
*object
)
2256 EMsgComposer
*composer
= E_MSG_COMPOSER (object
);
2258 e_composer_private_finalize (composer
);
2260 /* Chain up to parent's finalize() method. */
2261 G_OBJECT_CLASS (e_msg_composer_parent_class
)->finalize (object
);
2265 msg_composer_gallery_drag_data_get (GtkIconView
*icon_view
,
2266 GdkDragContext
*context
,
2267 GtkSelectionData
*selection_data
,
2272 GtkCellRenderer
*cell
;
2273 GtkTreeModel
*model
;
2278 if (!gtk_icon_view_get_cursor (icon_view
, &path
, &cell
))
2281 target
= gtk_selection_data_get_target (selection_data
);
2283 model
= gtk_icon_view_get_model (icon_view
);
2284 gtk_tree_model_get_iter (model
, &iter
, path
);
2285 gtk_tree_model_get (model
, &iter
, 1, &str_data
, -1);
2286 gtk_tree_path_free (path
);
2288 /* only supports "text/uri-list" */
2289 gtk_selection_data_set (
2290 selection_data
, target
, 8,
2291 (guchar
*) str_data
, strlen (str_data
));
2296 composer_notify_activity_cb (EActivityBar
*activity_bar
,
2298 EMsgComposer
*composer
)
2300 EHTMLEditor
*editor
;
2301 EHTMLEditorView
*view
;
2302 WebKitWebView
*web_view
;
2306 busy
= (e_activity_bar_get_activity (activity_bar
) != NULL
);
2308 if (busy
== composer
->priv
->busy
)
2311 composer
->priv
->busy
= busy
;
2314 e_msg_composer_save_focused_widget (composer
);
2316 editor
= e_msg_composer_get_editor (composer
);
2317 view
= e_html_editor_get_view (editor
);
2318 web_view
= WEBKIT_WEB_VIEW (view
);
2321 editable
= webkit_web_view_get_editable (web_view
);
2322 webkit_web_view_set_editable (web_view
, FALSE
);
2323 composer
->priv
->saved_editable
= editable
;
2325 editable
= composer
->priv
->saved_editable
;
2326 webkit_web_view_set_editable (web_view
, editable
);
2329 g_object_notify (G_OBJECT (composer
), "busy");
2332 e_msg_composer_restore_focus_on_composer (composer
);
2336 msg_composer_constructed (GObject
*object
)
2339 EMsgComposer
*composer
;
2340 EActivityBar
*activity_bar
;
2341 EAttachmentView
*view
;
2342 EAttachmentStore
*store
;
2343 EComposerHeaderTable
*table
;
2344 EHTMLEditor
*editor
;
2345 EHTMLEditorView
*html_editor_view
;
2346 GtkUIManager
*ui_manager
;
2347 GtkToggleAction
*action
;
2348 GtkTargetList
*target_list
;
2349 GtkTargetEntry
*targets
;
2351 GSettings
*settings
;
2355 composer
= E_MSG_COMPOSER (object
);
2357 shell
= e_msg_composer_get_shell (composer
);
2359 e_composer_private_constructed (composer
);
2361 editor
= e_msg_composer_get_editor (composer
);
2362 html_editor_view
= e_html_editor_get_view (editor
);
2363 ui_manager
= e_html_editor_get_ui_manager (editor
);
2364 view
= e_msg_composer_get_attachment_view (composer
);
2365 table
= E_COMPOSER_HEADER_TABLE (composer
->priv
->header_table
);
2367 gtk_window_set_title (GTK_WINDOW (composer
), _("Compose Message"));
2368 gtk_window_set_icon_name (GTK_WINDOW (composer
), "mail-message-new");
2369 gtk_window_set_default_size (GTK_WINDOW (composer
), 600, 500);
2372 object
, "delete-event",
2373 G_CALLBACK (msg_composer_delete_event_cb
), NULL
);
2377 G_CALLBACK (msg_composer_realize_cb
), NULL
);
2379 gtk_application_add_window (
2380 GTK_APPLICATION (shell
), GTK_WINDOW (object
));
2383 shell
, "quit-requested",
2384 G_CALLBACK (msg_composer_quit_requested_cb
), composer
);
2387 shell
, "prepare-for-quit",
2388 G_CALLBACK (msg_composer_prepare_for_quit_cb
), composer
);
2390 /* Restore Persistent State */
2393 GTK_WINDOW (composer
),
2394 "/org/gnome/evolution/mail/composer-window/",
2395 E_RESTORE_WINDOW_SIZE
);
2397 activity_bar
= e_html_editor_get_activity_bar (editor
);
2399 activity_bar
, "notify::activity",
2400 G_CALLBACK (composer_notify_activity_cb
), composer
);
2403 /* Honor User Preferences */
2405 /* FIXME This should be an EMsgComposer property. */
2406 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
2407 action
= GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT
));
2408 active
= g_settings_get_boolean (settings
, "composer-request-receipt");
2409 gtk_toggle_action_set_active (action
, active
);
2411 action
= GTK_TOGGLE_ACTION (ACTION (UNICODE_SMILEYS
));
2412 active
= g_settings_get_boolean (settings
, "composer-unicode-smileys");
2413 gtk_toggle_action_set_active (action
, active
);
2414 g_object_unref (settings
);
2416 /* Clipboard Support */
2419 html_editor_view
, "paste-clipboard",
2420 G_CALLBACK (msg_composer_paste_clipboard_cb
), composer
);
2423 html_editor_view
, "paste-primary-clipboard",
2424 G_CALLBACK (msg_composer_paste_primary_clipboard_cb
), composer
);
2426 /* Drag-and-Drop Support */
2429 html_editor_view
, "drag-motion",
2430 G_CALLBACK (msg_composer_drag_motion_cb
), composer
);
2433 html_editor_view
, "drag-drop",
2434 G_CALLBACK (msg_composer_drag_drop_cb
), composer
);
2437 html_editor_view
, "drag-data-received",
2438 G_CALLBACK (msg_composer_drag_data_received_cb
), composer
);
2440 /* Used for fixing various stuff after WebKit processed the DnD data. */
2441 g_signal_connect_after (
2442 html_editor_view
, "drag-data-received",
2443 G_CALLBACK (msg_composer_drag_data_received_after_cb
), composer
);
2446 composer
->priv
->gallery_icon_view
, "drag-data-get",
2447 G_CALLBACK (msg_composer_gallery_drag_data_get
), NULL
);
2449 /* Configure Headers */
2451 composer
->priv
->notify_destinations_bcc_handler
= e_signal_connect_notify_swapped (
2452 table
, "notify::destinations-bcc",
2453 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2454 composer
->priv
->notify_destinations_cc_handler
= e_signal_connect_notify_swapped (
2455 table
, "notify::destinations-cc",
2456 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2457 composer
->priv
->notify_destinations_to_handler
= e_signal_connect_notify_swapped (
2458 table
, "notify::destinations-to",
2459 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2460 composer
->priv
->notify_identity_uid_handler
= e_signal_connect_notify_swapped (
2461 table
, "notify::identity-uid",
2462 G_CALLBACK (msg_composer_mail_identity_changed_cb
), composer
);
2463 composer
->priv
->notify_reply_to_handler
= e_signal_connect_notify_swapped (
2464 table
, "notify::reply-to",
2465 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2466 composer
->priv
->notify_signature_uid_handler
= e_signal_connect_notify_swapped (
2467 table
, "notify::signature-uid",
2468 G_CALLBACK (e_composer_update_signature
), composer
);
2469 composer
->priv
->notify_subject_changed_handler
= e_signal_connect_notify_swapped (
2470 table
, "notify::subject",
2471 G_CALLBACK (msg_composer_subject_changed_cb
), composer
);
2472 composer
->priv
->notify_subject_handler
= e_signal_connect_notify_swapped (
2473 table
, "notify::subject",
2474 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2476 msg_composer_mail_identity_changed_cb (composer
);
2480 store
= e_attachment_view_get_store (view
);
2482 g_signal_connect_swapped (
2483 store
, "row-deleted",
2484 G_CALLBACK (attachment_store_changed_cb
), composer
);
2486 g_signal_connect_swapped (
2487 store
, "row-inserted",
2488 G_CALLBACK (attachment_store_changed_cb
), composer
);
2490 /* Initialization may have tripped the "changed" state. */
2491 e_html_editor_view_set_changed (html_editor_view
, FALSE
);
2493 target_list
= e_attachment_view_get_target_list (view
);
2494 targets
= gtk_target_table_new_from_list (target_list
, &n_targets
);
2496 target_list
= gtk_drag_dest_get_target_list (GTK_WIDGET (html_editor_view
));
2498 gtk_target_list_add_table (target_list
, drag_dest_targets
, G_N_ELEMENTS (drag_dest_targets
));
2499 gtk_target_list_add_table (target_list
, targets
, n_targets
);
2501 gtk_target_table_free (targets
, n_targets
);
2503 id
= "org.gnome.evolution.composer";
2504 e_plugin_ui_register_manager (ui_manager
, id
, composer
);
2505 e_plugin_ui_enable_manager (ui_manager
, id
);
2507 e_extensible_load_extensions (E_EXTENSIBLE (composer
));
2509 e_msg_composer_set_body_text (composer
, "", TRUE
);
2510 /* Chain up to parent's constructed() method. */
2511 G_OBJECT_CLASS (e_msg_composer_parent_class
)->constructed (object
);
2515 msg_composer_dispose (GObject
*object
)
2517 EMsgComposer
*composer
= E_MSG_COMPOSER (object
);
2518 EMsgComposerPrivate
*priv
= E_MSG_COMPOSER_GET_PRIVATE (composer
);
2521 if (priv
->address_dialog
!= NULL
) {
2522 gtk_widget_destroy (priv
->address_dialog
);
2523 priv
->address_dialog
= NULL
;
2526 /* FIXME Our EShell is already unreferenced. */
2527 shell
= e_shell_get_default ();
2529 g_signal_handlers_disconnect_by_func (
2530 shell
, msg_composer_quit_requested_cb
, composer
);
2531 g_signal_handlers_disconnect_by_func (
2532 shell
, msg_composer_prepare_for_quit_cb
, composer
);
2534 if (priv
->header_table
!= NULL
) {
2535 EComposerHeaderTable
*table
;
2537 table
= E_COMPOSER_HEADER_TABLE (composer
->priv
->header_table
);
2539 e_signal_disconnect_notify_handler (
2540 table
, &priv
->notify_destinations_bcc_handler
);
2541 e_signal_disconnect_notify_handler (
2542 table
, &priv
->notify_destinations_cc_handler
);
2543 e_signal_disconnect_notify_handler (
2544 table
, &priv
->notify_destinations_to_handler
);
2545 e_signal_disconnect_notify_handler (
2546 table
, &priv
->notify_identity_uid_handler
);
2547 e_signal_disconnect_notify_handler (
2548 table
, &priv
->notify_reply_to_handler
);
2549 e_signal_disconnect_notify_handler (
2550 table
, &priv
->notify_destinations_to_handler
);
2551 e_signal_disconnect_notify_handler (
2552 table
, &priv
->notify_subject_changed_handler
);
2555 e_composer_private_dispose (composer
);
2557 /* Chain up to parent's dispose() method. */
2558 G_OBJECT_CLASS (e_msg_composer_parent_class
)->dispose (object
);
2562 msg_composer_map (GtkWidget
*widget
)
2564 EMsgComposer
*composer
;
2565 EComposerHeaderTable
*table
;
2566 GtkWidget
*input_widget
;
2567 EHTMLEditor
*editor
;
2568 EHTMLEditorView
*view
;
2571 /* Chain up to parent's map() method. */
2572 GTK_WIDGET_CLASS (e_msg_composer_parent_class
)->map (widget
);
2574 composer
= E_MSG_COMPOSER (widget
);
2575 editor
= e_msg_composer_get_editor (composer
);
2576 table
= e_msg_composer_get_header_table (composer
);
2578 /* If the 'To' field is empty, focus it. */
2580 e_composer_header_table_get_header (
2581 table
, E_COMPOSER_HEADER_TO
)->input_widget
;
2582 text
= gtk_entry_get_text (GTK_ENTRY (input_widget
));
2583 if (gtk_widget_get_visible (input_widget
) && (text
== NULL
|| *text
== '\0')) {
2584 gtk_widget_grab_focus (input_widget
);
2588 /* If not, check the 'Subject' field. */
2590 e_composer_header_table_get_header (
2591 table
, E_COMPOSER_HEADER_SUBJECT
)->input_widget
;
2592 text
= gtk_entry_get_text (GTK_ENTRY (input_widget
));
2593 if (gtk_widget_get_visible (input_widget
) && (text
== NULL
|| *text
== '\0')) {
2594 gtk_widget_grab_focus (input_widget
);
2598 /* Jump to the editor as a last resort. */
2599 view
= e_html_editor_get_view (editor
);
2600 gtk_widget_grab_focus (GTK_WIDGET (view
));
2604 msg_composer_key_press_event (GtkWidget
*widget
,
2607 EMsgComposer
*composer
;
2608 GtkWidget
*input_widget
;
2609 EHTMLEditor
*editor
;
2610 EHTMLEditorView
*view
;
2612 composer
= E_MSG_COMPOSER (widget
);
2613 editor
= e_msg_composer_get_editor (composer
);
2614 view
= e_html_editor_get_view (editor
);
2617 e_composer_header_table_get_header (
2618 e_msg_composer_get_header_table (composer
),
2619 E_COMPOSER_HEADER_SUBJECT
)->input_widget
;
2622 if (event
->keyval
== XF86XK_Send
) {
2623 e_msg_composer_send (composer
);
2626 #endif /* HAVE_XFREE */
2628 if (event
->keyval
== GDK_KEY_Escape
) {
2629 gtk_action_activate (ACTION (CLOSE
));
2633 if (event
->keyval
== GDK_KEY_Tab
&& gtk_widget_is_focus (input_widget
)) {
2634 gtk_widget_grab_focus (GTK_WIDGET (view
));
2638 if (gtk_widget_is_focus (GTK_WIDGET (view
))) {
2639 if (event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
2640 gboolean view_processed
= FALSE
;
2642 g_signal_emit_by_name (view
, "key-press-event", event
, &view_processed
);
2644 if (!view_processed
)
2645 gtk_widget_grab_focus (input_widget
);
2650 if ((((event
)->state
& GDK_SHIFT_MASK
) &&
2651 ((event
)->keyval
== GDK_KEY_Insert
)) ||
2652 (((event
)->state
& GDK_CONTROL_MASK
) &&
2653 ((event
)->keyval
== GDK_KEY_v
))) {
2654 g_signal_emit_by_name (
2655 WEBKIT_WEB_VIEW (view
), "paste-clipboard");
2659 if (((event
)->state
& GDK_CONTROL_MASK
) &&
2660 ((event
)->keyval
== GDK_KEY_Insert
)) {
2661 g_signal_emit_by_name (
2662 WEBKIT_WEB_VIEW (view
), "copy-clipboard");
2666 if (((event
)->state
& GDK_CONTROL_MASK
) &&
2667 ((event
)->keyval
== GDK_KEY_z
)) {
2668 e_html_editor_view_undo (view
);
2672 if (((event
)->state
& (GDK_CONTROL_MASK
)) &&
2673 ((event
)->keyval
== GDK_KEY_Z
)) {
2674 e_html_editor_view_redo (view
);
2678 if (((event
)->state
& GDK_SHIFT_MASK
) &&
2679 ((event
)->keyval
== GDK_KEY_Delete
)) {
2680 g_signal_emit_by_name (
2681 WEBKIT_WEB_VIEW (view
), "cut-clipboard");
2686 /* Chain up to parent's key_press_event() method. */
2687 return GTK_WIDGET_CLASS (e_msg_composer_parent_class
)->
2688 key_press_event (widget
, event
);
2692 msg_composer_presend (EMsgComposer
*composer
)
2694 /* This keeps the signal accumulator at TRUE. */
2699 msg_composer_accumulator_false_abort (GSignalInvocationHint
*ihint
,
2700 GValue
*return_accu
,
2701 const GValue
*handler_return
,
2706 v_boolean
= g_value_get_boolean (handler_return
);
2707 g_value_set_boolean (return_accu
, v_boolean
);
2709 /* FALSE means abort the signal emission. */
2714 * e_msg_composer_is_busy:
2715 * @composer: an #EMsgComposer
2717 * Returns %TRUE only while an #EActivity is in progress.
2719 * Returns: whether @composer is busy
2722 e_msg_composer_is_busy (EMsgComposer
*composer
)
2724 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), FALSE
);
2726 return composer
->priv
->busy
;
2730 e_msg_composer_class_init (EMsgComposerClass
*class)
2732 GObjectClass
*object_class
;
2733 GtkWidgetClass
*widget_class
;
2735 g_type_class_add_private (class, sizeof (EMsgComposerPrivate
));
2737 object_class
= G_OBJECT_CLASS (class);
2738 object_class
->set_property
= msg_composer_set_property
;
2739 object_class
->get_property
= msg_composer_get_property
;
2740 object_class
->dispose
= msg_composer_dispose
;
2741 object_class
->finalize
= msg_composer_finalize
;
2742 object_class
->constructed
= msg_composer_constructed
;
2744 widget_class
= GTK_WIDGET_CLASS (class);
2745 widget_class
->map
= msg_composer_map
;
2746 widget_class
->key_press_event
= msg_composer_key_press_event
;
2748 class->presend
= msg_composer_presend
;
2750 g_object_class_install_property (
2753 g_param_spec_boolean (
2756 "Whether an activity is in progress",
2759 G_PARAM_STATIC_STRINGS
));
2761 g_object_class_install_property (
2764 g_param_spec_object (
2768 E_TYPE_FOCUS_TRACKER
,
2771 g_object_class_install_property (
2774 g_param_spec_object (
2777 "The EShell singleton",
2780 G_PARAM_CONSTRUCT_ONLY
));
2782 signals
[PRESEND
] = g_signal_new (
2784 G_OBJECT_CLASS_TYPE (class),
2786 G_STRUCT_OFFSET (EMsgComposerClass
, presend
),
2787 msg_composer_accumulator_false_abort
,
2789 e_marshal_BOOLEAN__VOID
,
2792 signals
[SEND
] = g_signal_new (
2794 G_OBJECT_CLASS_TYPE (class),
2796 G_STRUCT_OFFSET (EMsgComposerClass
, send
),
2798 e_marshal_VOID__OBJECT_OBJECT
,
2800 CAMEL_TYPE_MIME_MESSAGE
,
2803 signals
[SAVE_TO_DRAFTS
] = g_signal_new (
2805 G_OBJECT_CLASS_TYPE (class),
2807 G_STRUCT_OFFSET (EMsgComposerClass
, save_to_drafts
),
2809 e_marshal_VOID__OBJECT_OBJECT
,
2811 CAMEL_TYPE_MIME_MESSAGE
,
2814 signals
[SAVE_TO_OUTBOX
] = g_signal_new (
2816 G_OBJECT_CLASS_TYPE (class),
2818 G_STRUCT_OFFSET (EMsgComposerClass
, save_to_outbox
),
2820 e_marshal_VOID__OBJECT_OBJECT
,
2822 CAMEL_TYPE_MIME_MESSAGE
,
2825 signals
[PRINT
] = g_signal_new (
2827 G_OBJECT_CLASS_TYPE (class),
2830 e_marshal_VOID__ENUM_OBJECT_OBJECT
,
2832 GTK_TYPE_PRINT_OPERATION_ACTION
,
2833 CAMEL_TYPE_MIME_MESSAGE
,
2838 e_msg_composer_init (EMsgComposer
*composer
)
2840 EHTMLEditorView
*view
;
2842 composer
->priv
= E_MSG_COMPOSER_GET_PRIVATE (composer
);
2844 composer
->priv
->editor
= g_object_ref_sink (e_html_editor_new ());
2845 view
= e_html_editor_get_view (composer
->priv
->editor
);
2846 e_html_editor_view_set_is_editting_message (view
, TRUE
);
2850 * e_msg_composer_new:
2851 * @shell: an #EShell
2853 * Create a new message composer widget.
2855 * Returns: A pointer to the newly created widget
2858 e_msg_composer_new (EShell
*shell
)
2860 g_return_val_if_fail (E_IS_SHELL (shell
), NULL
);
2862 return g_object_new (
2863 E_TYPE_MSG_COMPOSER
,
2864 "shell", shell
, NULL
);
2868 * e_msg_composer_get_editor:
2869 * @composer: an #EMsgComposer
2871 * Returns @composer's internal #EHTMLEditor instance.
2873 * Returns: an #EHTMLEditor
2876 e_msg_composer_get_editor (EMsgComposer
*composer
)
2878 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
2880 return composer
->priv
->editor
;
2884 e_msg_composer_get_focus_tracker (EMsgComposer
*composer
)
2886 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
2888 return composer
->priv
->focus_tracker
;
2892 e_msg_composer_set_pending_body (EMsgComposer
*composer
,
2897 g_object_set_data_full (
2898 G_OBJECT (composer
), "body:text_mime_type",
2899 GINT_TO_POINTER (is_html
), NULL
);
2900 g_object_set_data_full (
2901 G_OBJECT (composer
), "body:text",
2902 text
, (GDestroyNotify
) g_free
);
2906 e_msg_composer_flush_pending_body (EMsgComposer
*composer
)
2911 body
= g_object_get_data (G_OBJECT (composer
), "body:text");
2912 is_html
= GPOINTER_TO_INT (
2913 g_object_get_data (G_OBJECT (composer
), "body:text_mime_type"));
2916 set_editor_text (composer
, body
, is_html
, FALSE
);
2918 g_object_set_data (G_OBJECT (composer
), "body:text", NULL
);
2922 add_attachments_handle_mime_part (EMsgComposer
*composer
,
2923 CamelMimePart
*mime_part
,
2924 gboolean just_inlines
,
2928 CamelContentType
*content_type
;
2929 CamelDataWrapper
*wrapper
;
2930 EHTMLEditor
*editor
;
2931 EHTMLEditorView
*view
;
2936 content_type
= camel_mime_part_get_content_type (mime_part
);
2937 wrapper
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
2938 editor
= e_msg_composer_get_editor (composer
);
2939 view
= e_html_editor_get_view (editor
);
2941 if (CAMEL_IS_MULTIPART (wrapper
)) {
2942 /* another layer of multipartness... */
2943 add_attachments_from_multipart (
2944 composer
, (CamelMultipart
*) wrapper
,
2945 just_inlines
, depth
+ 1);
2946 } else if (just_inlines
) {
2947 if (camel_mime_part_get_content_id (mime_part
) ||
2948 camel_mime_part_get_content_location (mime_part
))
2949 e_html_editor_view_add_inline_image_from_mime_part (
2951 } else if (related
&& camel_content_type_is (content_type
, "image", "*")) {
2952 e_html_editor_view_add_inline_image_from_mime_part (view
, mime_part
);
2953 } else if (camel_content_type_is (content_type
, "text", "*") &&
2954 camel_mime_part_get_filename (mime_part
) == NULL
) {
2955 /* Do nothing if this is a text/anything without a
2956 * filename, otherwise attach it too. */
2958 e_msg_composer_attach (composer
, mime_part
);
2963 add_attachments_from_multipart (EMsgComposer
*composer
,
2964 CamelMultipart
*multipart
,
2965 gboolean just_inlines
,
2968 /* find appropriate message attachments to add to the composer */
2969 CamelMimePart
*mime_part
;
2973 related
= camel_content_type_is (
2974 CAMEL_DATA_WRAPPER (multipart
)->mime_type
,
2975 "multipart", "related");
2977 if (CAMEL_IS_MULTIPART_SIGNED (multipart
)) {
2978 mime_part
= camel_multipart_get_part (
2979 multipart
, CAMEL_MULTIPART_SIGNED_CONTENT
);
2980 add_attachments_handle_mime_part (
2981 composer
, mime_part
, just_inlines
, related
, depth
);
2982 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (multipart
)) {
2983 /* XXX What should we do in this case? */
2985 nparts
= camel_multipart_get_number (multipart
);
2987 for (i
= 0; i
< nparts
; i
++) {
2988 mime_part
= camel_multipart_get_part (multipart
, i
);
2989 add_attachments_handle_mime_part (
2990 composer
, mime_part
, just_inlines
,
2997 * e_msg_composer_add_message_attachments:
2998 * @composer: the composer to add the attachments to.
2999 * @message: the source message to copy the attachments from.
3000 * @just_inlines: whether to attach all attachments or just add
3003 * Walk through all the mime parts in @message and add them to the composer
3004 * specified in @composer.
3007 e_msg_composer_add_message_attachments (EMsgComposer
*composer
,
3008 CamelMimeMessage
*message
,
3009 gboolean just_inlines
)
3011 CamelDataWrapper
*wrapper
;
3013 wrapper
= camel_medium_get_content (CAMEL_MEDIUM (message
));
3014 if (!CAMEL_IS_MULTIPART (wrapper
))
3017 add_attachments_from_multipart (
3018 composer
, (CamelMultipart
*) wrapper
, just_inlines
, 0);
3022 handle_multipart_signed (EMsgComposer
*composer
,
3023 CamelMultipart
*multipart
,
3024 gboolean keep_signature
,
3025 GCancellable
*cancellable
,
3028 CamelContentType
*content_type
;
3029 CamelDataWrapper
*content
;
3030 CamelMimePart
*mime_part
;
3031 GtkToggleAction
*action
= NULL
;
3032 const gchar
*protocol
;
3034 content
= CAMEL_DATA_WRAPPER (multipart
);
3035 content_type
= camel_data_wrapper_get_mime_type_field (content
);
3036 protocol
= camel_content_type_param (content_type
, "protocol");
3038 if (protocol
== NULL
) {
3040 } else if (g_ascii_strcasecmp (protocol
, "application/pgp-signature") == 0) {
3041 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
))) &&
3042 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
))))
3043 action
= GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
));
3044 } else if (g_ascii_strcasecmp (protocol
, "application/x-pkcs7-signature") == 0) {
3045 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
))) &&
3046 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
))))
3047 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
));
3051 gtk_toggle_action_set_active (action
, TRUE
);
3053 mime_part
= camel_multipart_get_part (
3054 multipart
, CAMEL_MULTIPART_SIGNED_CONTENT
);
3056 if (mime_part
== NULL
)
3059 content_type
= camel_mime_part_get_content_type (mime_part
);
3060 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
3062 if (CAMEL_IS_MULTIPART (content
)) {
3063 multipart
= CAMEL_MULTIPART (content
);
3065 /* Note: depth is preserved here because we're not
3066 * counting multipart/signed as a multipart, instead
3067 * we want to treat the content part as our mime part
3070 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3071 /* Handle the signed content and configure
3072 * the composer to sign outgoing messages. */
3073 handle_multipart_signed (
3074 composer
, multipart
, keep_signature
, cancellable
, depth
);
3076 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3077 /* Decrypt the encrypted content and configure
3078 * the composer to encrypt outgoing messages. */
3079 handle_multipart_encrypted (
3080 composer
, mime_part
, keep_signature
, cancellable
, depth
);
3082 } else if (camel_content_type_is (content_type
, "multipart", "alternative")) {
3083 /* This contains the text/plain and text/html
3084 * versions of the message body. */
3085 handle_multipart_alternative (
3086 composer
, multipart
, keep_signature
, cancellable
, depth
);
3089 /* There must be attachments... */
3091 composer
, multipart
, keep_signature
, cancellable
, depth
);
3094 } else if (camel_content_type_is (content_type
, "text", "*")) {
3098 html
= emcu_part_to_html (
3099 composer
, mime_part
, &length
, keep_signature
, cancellable
);
3101 e_msg_composer_set_pending_body (composer
, html
, length
, TRUE
);
3104 e_msg_composer_attach (composer
, mime_part
);
3109 handle_multipart_encrypted (EMsgComposer
*composer
,
3110 CamelMimePart
*multipart
,
3111 gboolean keep_signature
,
3112 GCancellable
*cancellable
,
3115 CamelContentType
*content_type
;
3116 CamelCipherContext
*cipher
;
3117 CamelDataWrapper
*content
;
3118 CamelMimePart
*mime_part
;
3119 CamelSession
*session
;
3120 CamelCipherValidity
*valid
;
3121 GtkToggleAction
*action
= NULL
;
3122 const gchar
*protocol
;
3124 content_type
= camel_mime_part_get_content_type (multipart
);
3125 protocol
= camel_content_type_param (content_type
, "protocol");
3127 if (protocol
&& g_ascii_strcasecmp (protocol
, "application/pgp-encrypted") == 0) {
3128 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
))) &&
3129 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
))))
3130 action
= GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
));
3131 } else if (content_type
&& (
3132 camel_content_type_is (content_type
, "application", "x-pkcs7-mime")
3133 || camel_content_type_is (content_type
, "application", "pkcs7-mime"))) {
3134 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
))) &&
3135 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
))))
3136 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
));
3140 gtk_toggle_action_set_active (action
, TRUE
);
3142 session
= e_msg_composer_ref_session (composer
);
3143 cipher
= camel_gpg_context_new (session
);
3144 mime_part
= camel_mime_part_new ();
3145 valid
= camel_cipher_context_decrypt_sync (
3146 cipher
, multipart
, mime_part
, cancellable
, NULL
);
3147 g_object_unref (cipher
);
3148 g_object_unref (session
);
3153 camel_cipher_validity_free (valid
);
3155 content_type
= camel_mime_part_get_content_type (mime_part
);
3157 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
3159 if (CAMEL_IS_MULTIPART (content
)) {
3160 CamelMultipart
*content_multipart
= CAMEL_MULTIPART (content
);
3162 /* Note: depth is preserved here because we're not
3163 * counting multipart/encrypted as a multipart, instead
3164 * we want to treat the content part as our mime part
3167 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3168 /* Handle the signed content and configure the
3169 * composer to sign outgoing messages. */
3170 handle_multipart_signed (
3171 composer
, content_multipart
, keep_signature
, cancellable
, depth
);
3173 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3174 /* Decrypt the encrypted content and configure the
3175 * composer to encrypt outgoing messages. */
3176 handle_multipart_encrypted (
3177 composer
, mime_part
, keep_signature
, cancellable
, depth
);
3179 } else if (camel_content_type_is (content_type
, "multipart", "alternative")) {
3180 /* This contains the text/plain and text/html
3181 * versions of the message body. */
3182 handle_multipart_alternative (
3183 composer
, content_multipart
, keep_signature
, cancellable
, depth
);
3186 /* There must be attachments... */
3188 composer
, content_multipart
, keep_signature
, cancellable
, depth
);
3191 } else if (camel_content_type_is (content_type
, "text", "*")) {
3195 html
= emcu_part_to_html (
3196 composer
, mime_part
, &length
, keep_signature
, cancellable
);
3198 e_msg_composer_set_pending_body (composer
, html
, length
, TRUE
);
3201 e_msg_composer_attach (composer
, mime_part
);
3204 g_object_unref (mime_part
);
3208 handle_multipart_alternative (EMsgComposer
*composer
,
3209 CamelMultipart
*multipart
,
3210 gboolean keep_signature
,
3211 GCancellable
*cancellable
,
3214 /* Find the text/html part and set the composer body to its content */
3215 CamelMimePart
*text_part
= NULL
, *fallback_text_part
= NULL
;
3218 nparts
= camel_multipart_get_number (multipart
);
3220 for (i
= 0; i
< nparts
; i
++) {
3221 CamelContentType
*content_type
;
3222 CamelDataWrapper
*content
;
3223 CamelMimePart
*mime_part
;
3225 mime_part
= camel_multipart_get_part (multipart
, i
);
3230 content_type
= camel_mime_part_get_content_type (mime_part
);
3231 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
3233 if (CAMEL_IS_MULTIPART (content
)) {
3236 mp
= CAMEL_MULTIPART (content
);
3238 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3239 /* Handle the signed content and configure
3240 * the composer to sign outgoing messages. */
3241 handle_multipart_signed (
3242 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3244 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3245 /* Decrypt the encrypted content and configure
3246 * the composer to encrypt outgoing messages. */
3247 handle_multipart_encrypted (
3248 composer
, mime_part
, keep_signature
,
3249 cancellable
, depth
+ 1);
3252 /* Depth doesn't matter so long as we
3255 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3258 } else if (camel_content_type_is (content_type
, "text", "html")) {
3259 /* text/html is preferable, so once we find it we're done... */
3260 text_part
= mime_part
;
3262 } else if (camel_content_type_is (content_type
, "text", "*")) {
3263 /* anyt text part not text/html is second rate so the first
3264 * text part we find isn't necessarily the one we'll use. */
3266 text_part
= mime_part
;
3268 /* this is when prefer-plain filters out text/html part, then
3269 * the text/plain should be used */
3270 if (camel_content_type_is (content_type
, "text", "plain"))
3271 fallback_text_part
= mime_part
;
3273 e_msg_composer_attach (composer
, mime_part
);
3281 html
= emcu_part_to_html (
3282 composer
, text_part
, &length
, keep_signature
, cancellable
);
3283 if (!html
&& fallback_text_part
)
3284 html
= emcu_part_to_html (
3285 composer
, fallback_text_part
, &length
, keep_signature
, cancellable
);
3287 e_msg_composer_set_pending_body (composer
, html
, length
, TRUE
);
3292 handle_multipart (EMsgComposer
*composer
,
3293 CamelMultipart
*multipart
,
3294 gboolean keep_signature
,
3295 GCancellable
*cancellable
,
3300 nparts
= camel_multipart_get_number (multipart
);
3302 for (i
= 0; i
< nparts
; i
++) {
3303 CamelContentType
*content_type
;
3304 CamelDataWrapper
*content
;
3305 CamelMimePart
*mime_part
;
3307 mime_part
= camel_multipart_get_part (multipart
, i
);
3312 content_type
= camel_mime_part_get_content_type (mime_part
);
3313 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
3315 if (CAMEL_IS_MULTIPART (content
)) {
3318 mp
= CAMEL_MULTIPART (content
);
3320 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3321 /* Handle the signed content and configure
3322 * the composer to sign outgoing messages. */
3323 handle_multipart_signed (
3324 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3326 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3327 /* Decrypt the encrypted content and configure
3328 * the composer to encrypt outgoing messages. */
3329 handle_multipart_encrypted (
3330 composer
, mime_part
, keep_signature
,
3331 cancellable
, depth
+ 1);
3333 } else if (camel_content_type_is (
3334 content_type
, "multipart", "alternative")) {
3335 handle_multipart_alternative (
3336 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3339 /* Depth doesn't matter so long as we
3342 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3345 } else if (depth
== 0 && i
== 0) {
3346 EHTMLEditor
*editor
;
3347 gboolean is_message_from_draft
, is_html
= FALSE
;
3351 editor
= e_msg_composer_get_editor (composer
);
3352 is_message_from_draft
= e_html_editor_view_is_message_from_draft (
3353 e_html_editor_get_view (editor
));
3354 is_html
= camel_content_type_is (content_type
, "text", "html");
3356 /* Since the first part is not multipart/alternative,
3357 * this must be the body. */
3359 /* If we are opening message from Drafts */
3360 if (is_message_from_draft
) {
3361 /* Extract the body */
3362 CamelDataWrapper
*dw
;
3364 dw
= camel_medium_get_content ((CamelMedium
*) mime_part
);
3366 CamelStream
*mem
= camel_stream_mem_new ();
3369 camel_data_wrapper_decode_to_stream_sync (dw
, mem
, cancellable
, NULL
);
3370 camel_stream_close (mem
, cancellable
, NULL
);
3372 bytes
= camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem
));
3373 if (bytes
&& bytes
->len
)
3374 html
= g_strndup ((const gchar
*) bytes
->data
, bytes
->len
);
3376 g_object_unref (mem
);
3380 html
= emcu_part_to_html (
3381 composer
, mime_part
, &length
, keep_signature
, cancellable
);
3385 e_msg_composer_set_pending_body (composer
, html
, length
, is_html
);
3387 } else if (camel_mime_part_get_content_id (mime_part
) ||
3388 camel_mime_part_get_content_location (mime_part
)) {
3389 /* special in-line attachment */
3390 EHTMLEditor
*editor
;
3392 editor
= e_msg_composer_get_editor (composer
);
3393 e_html_editor_view_add_inline_image_from_mime_part (
3394 e_html_editor_get_view (editor
), mime_part
);
3397 /* normal attachment */
3398 e_msg_composer_attach (composer
, mime_part
);
3404 set_signature_gui (EMsgComposer
*composer
)
3406 EHTMLEditor
*editor
;
3407 EHTMLEditorView
*view
;
3408 WebKitDOMDocument
*document
;
3409 WebKitDOMNodeList
*nodes
;
3410 EComposerHeaderTable
*table
;
3411 EMailSignatureComboBox
*combo_box
;
3415 table
= e_msg_composer_get_header_table (composer
);
3416 combo_box
= e_composer_header_table_get_signature_combo_box (table
);
3418 editor
= e_msg_composer_get_editor (composer
);
3419 view
= e_html_editor_get_view (editor
);
3420 document
= webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view
));
3423 nodes
= webkit_dom_document_get_elements_by_class_name (
3424 document
, "-x-evo-signature");
3425 length
= webkit_dom_node_list_get_length (nodes
);
3426 for (ii
= 0; ii
< length
; ii
++) {
3427 WebKitDOMNode
*node
;
3430 node
= webkit_dom_node_list_item (nodes
, ii
);
3431 id
= webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (node
));
3432 if (id
&& (strlen (id
) == 1) && (*id
== '1')) {
3433 uid
= webkit_dom_element_get_attribute (
3434 WEBKIT_DOM_ELEMENT (node
), "name");
3436 g_object_unref (node
);
3440 g_object_unref (node
);
3443 g_object_unref (nodes
);
3445 /* The combo box active ID is the signature's ESource UID. */
3447 gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box
), uid
);
3453 composer_add_auto_recipients (ESource
*source
,
3454 const gchar
*property_name
,
3455 GHashTable
*hash_table
)
3457 ESourceMailComposition
*extension
;
3458 CamelInternetAddress
*inet_addr
;
3459 const gchar
*extension_name
;
3460 gchar
*comma_separated_addrs
;
3461 gchar
**addr_array
= NULL
;
3465 extension_name
= E_SOURCE_EXTENSION_MAIL_COMPOSITION
;
3466 extension
= e_source_get_extension (source
, extension_name
);
3468 g_object_get (extension
, property_name
, &addr_array
, NULL
);
3470 if (addr_array
== NULL
)
3473 inet_addr
= camel_internet_address_new ();
3474 comma_separated_addrs
= g_strjoinv (", ", addr_array
);
3476 retval
= camel_address_decode (
3477 CAMEL_ADDRESS (inet_addr
), comma_separated_addrs
);
3479 g_free (comma_separated_addrs
);
3480 g_strfreev (addr_array
);
3485 length
= camel_address_length (CAMEL_ADDRESS (inet_addr
));
3487 for (ii
= 0; ii
< length
; ii
++) {
3491 if (camel_internet_address_get (inet_addr
, ii
, &name
, &addr
))
3492 g_hash_table_add (hash_table
, g_strdup (addr
));
3495 g_object_unref (inet_addr
);
3499 * e_msg_composer_new_with_message:
3500 * @shell: an #EShell
3501 * @message: The message to use as the source
3502 * @keep_signature: Keep message signature, if any
3503 * @override_identity_uid: (allow none): Optional identity UID to use, or %NULL
3504 * @cancellable: optional #GCancellable object, or %NULL
3506 * Create a new message composer widget.
3508 * Note: Designed to work only for messages constructed using Evolution.
3510 * Returns: A pointer to the newly created widget
3513 e_msg_composer_new_with_message (EShell
*shell
,
3514 CamelMimeMessage
*message
,
3515 gboolean keep_signature
,
3516 const gchar
*override_identity_uid
,
3517 GCancellable
*cancellable
)
3519 CamelInternetAddress
*from
, *to
, *cc
, *bcc
;
3520 GList
*To
= NULL
, *Cc
= NULL
, *Bcc
= NULL
, *postto
= NULL
;
3521 const gchar
*format
, *subject
, *composer_mode
;
3522 EDestination
**Tov
, **Ccv
, **Bccv
;
3523 GHashTable
*auto_cc
, *auto_bcc
;
3524 CamelContentType
*content_type
;
3525 struct _camel_header_raw
*headers
;
3526 CamelDataWrapper
*content
;
3527 EMsgComposer
*composer
;
3528 EMsgComposerPrivate
*priv
;
3529 EComposerHeaderTable
*table
;
3530 ESource
*source
= NULL
;
3531 EHTMLEditor
*editor
;
3532 EHTMLEditorView
*view
;
3533 GtkToggleAction
*action
;
3534 struct _camel_header_raw
*xev
;
3535 gchar
*identity_uid
;
3537 gboolean is_message_from_draft
= FALSE
;
3539 g_return_val_if_fail (E_IS_SHELL (shell
), NULL
);
3541 headers
= CAMEL_MIME_PART (message
)->headers
;
3542 while (headers
!= NULL
) {
3545 if (strcmp (headers
->name
, "X-Evolution-PostTo") == 0) {
3546 value
= g_strstrip (g_strdup (headers
->value
));
3547 postto
= g_list_append (postto
, value
);
3550 headers
= headers
->next
;
3553 composer
= e_msg_composer_new (shell
);
3554 priv
= E_MSG_COMPOSER_GET_PRIVATE (composer
);
3555 editor
= e_msg_composer_get_editor (composer
);
3556 table
= e_msg_composer_get_header_table (composer
);
3557 view
= e_html_editor_get_view (editor
);
3560 e_composer_header_table_set_post_to_list (table
, postto
);
3561 g_list_foreach (postto
, (GFunc
) g_free
, NULL
);
3562 g_list_free (postto
);
3566 if (override_identity_uid
&& *override_identity_uid
) {
3567 identity_uid
= (gchar
*) override_identity_uid
;
3569 /* Restore the mail identity preference. */
3570 identity_uid
= (gchar
*) camel_medium_get_header (
3571 CAMEL_MEDIUM (message
), "X-Evolution-Identity");
3572 if (!identity_uid
) {
3573 /* for backward compatibility */
3574 identity_uid
= (gchar
*) camel_medium_get_header (
3575 CAMEL_MEDIUM (message
), "X-Evolution-Account");
3577 if (!identity_uid
) {
3578 source
= em_utils_guess_mail_identity_with_recipients (
3579 e_shell_get_registry (shell
), message
, NULL
, NULL
);
3581 identity_uid
= e_source_dup_uid (source
);
3585 if (identity_uid
!= NULL
&& !source
) {
3586 identity_uid
= g_strstrip (g_strdup (identity_uid
));
3587 source
= e_composer_header_table_ref_source (
3588 table
, identity_uid
);
3591 auto_cc
= g_hash_table_new_full (
3592 (GHashFunc
) camel_strcase_hash
,
3593 (GEqualFunc
) camel_strcase_equal
,
3594 (GDestroyNotify
) g_free
,
3595 (GDestroyNotify
) NULL
);
3597 auto_bcc
= g_hash_table_new_full (
3598 (GHashFunc
) camel_strcase_hash
,
3599 (GEqualFunc
) camel_strcase_equal
,
3600 (GDestroyNotify
) g_free
,
3601 (GDestroyNotify
) NULL
);
3603 if (source
!= NULL
) {
3604 composer_add_auto_recipients (source
, "cc", auto_cc
);
3605 composer_add_auto_recipients (source
, "bcc", auto_bcc
);
3608 to
= camel_mime_message_get_recipients (message
, CAMEL_RECIPIENT_TYPE_TO
);
3609 cc
= camel_mime_message_get_recipients (message
, CAMEL_RECIPIENT_TYPE_CC
);
3610 bcc
= camel_mime_message_get_recipients (message
, CAMEL_RECIPIENT_TYPE_BCC
);
3612 len
= CAMEL_ADDRESS (to
)->addresses
->len
;
3613 for (i
= 0; i
< len
; i
++) {
3614 const gchar
*name
, *addr
;
3616 if (camel_internet_address_get (to
, i
, &name
, &addr
)) {
3617 EDestination
*dest
= e_destination_new ();
3618 e_destination_set_name (dest
, name
);
3619 e_destination_set_email (dest
, addr
);
3620 To
= g_list_append (To
, dest
);
3624 Tov
= destination_list_to_vector (To
);
3627 len
= CAMEL_ADDRESS (cc
)->addresses
->len
;
3628 for (i
= 0; i
< len
; i
++) {
3629 const gchar
*name
, *addr
;
3631 if (camel_internet_address_get (cc
, i
, &name
, &addr
)) {
3632 EDestination
*dest
= e_destination_new ();
3633 e_destination_set_name (dest
, name
);
3634 e_destination_set_email (dest
, addr
);
3636 if (g_hash_table_contains (auto_cc
, addr
))
3637 e_destination_set_auto_recipient (dest
, TRUE
);
3639 Cc
= g_list_append (Cc
, dest
);
3643 Ccv
= destination_list_to_vector (Cc
);
3644 g_hash_table_destroy (auto_cc
);
3647 len
= CAMEL_ADDRESS (bcc
)->addresses
->len
;
3648 for (i
= 0; i
< len
; i
++) {
3649 const gchar
*name
, *addr
;
3651 if (camel_internet_address_get (bcc
, i
, &name
, &addr
)) {
3652 EDestination
*dest
= e_destination_new ();
3653 e_destination_set_name (dest
, name
);
3654 e_destination_set_email (dest
, addr
);
3656 if (g_hash_table_contains (auto_bcc
, addr
))
3657 e_destination_set_auto_recipient (dest
, TRUE
);
3659 Bcc
= g_list_append (Bcc
, dest
);
3663 Bccv
= destination_list_to_vector (Bcc
);
3664 g_hash_table_destroy (auto_bcc
);
3668 g_object_unref (source
);
3670 subject
= camel_mime_message_get_subject (message
);
3672 e_composer_header_table_set_identity_uid (table
, identity_uid
);
3673 e_composer_header_table_set_destinations_to (table
, Tov
);
3674 e_composer_header_table_set_destinations_cc (table
, Ccv
);
3675 e_composer_header_table_set_destinations_bcc (table
, Bccv
);
3676 e_composer_header_table_set_subject (table
, subject
);
3678 g_free (identity_uid
);
3680 e_destination_freev (Tov
);
3681 e_destination_freev (Ccv
);
3682 e_destination_freev (Bccv
);
3684 from
= camel_mime_message_get_from (message
);
3685 if ((!override_identity_uid
|| !*override_identity_uid
) && from
) {
3686 const gchar
*name
= NULL
, *address
= NULL
;
3688 if (camel_address_length (CAMEL_ADDRESS (from
)) == 1 &&
3689 camel_internet_address_get (from
, 0, &name
, &address
)) {
3690 EComposerFromHeader
*header_from
;
3691 const gchar
*filled_name
, *filled_address
;
3693 header_from
= E_COMPOSER_FROM_HEADER (e_composer_header_table_get_header (table
, E_COMPOSER_HEADER_FROM
));
3695 filled_name
= e_composer_from_header_get_name (header_from
);
3696 filled_address
= e_composer_from_header_get_address (header_from
);
3701 if (address
&& !*address
)
3704 if (g_strcmp0 (filled_name
, name
) != 0 ||
3705 g_strcmp0 (filled_address
, address
) != 0) {
3706 e_composer_from_header_set_name (header_from
, name
);
3707 e_composer_from_header_set_address (header_from
, address
);
3708 e_composer_from_header_set_override_visible (header_from
, TRUE
);
3713 /* Restore the format editing preference */
3714 format
= camel_medium_get_header (
3715 CAMEL_MEDIUM (message
), "X-Evolution-Format");
3717 composer_mode
= camel_medium_get_header (
3718 CAMEL_MEDIUM (message
), "X-Evolution-Composer-Mode");
3720 if (composer_mode
&& *composer_mode
) {
3721 is_message_from_draft
= TRUE
;
3722 e_html_editor_view_set_is_message_from_draft (view
, TRUE
);
3725 if (format
!= NULL
) {
3728 while (*format
&& camel_mime_is_lwsp (*format
))
3731 flags
= g_strsplit (format
, ", ", 0);
3732 for (i
= 0; flags
[i
]; i
++) {
3733 if (g_ascii_strcasecmp (flags
[i
], "text/html") == 0) {
3734 if (composer_mode
&& g_ascii_strcasecmp (composer_mode
, "text/html") == 0) {
3735 e_html_editor_view_set_html_mode (
3738 e_html_editor_view_set_html_mode (
3741 } else if (g_ascii_strcasecmp (flags
[i
], "text/plain") == 0) {
3742 if (composer_mode
&& g_ascii_strcasecmp (composer_mode
, "text/html") == 0) {
3743 e_html_editor_view_set_html_mode (
3746 e_html_editor_view_set_html_mode (
3749 } else if (g_ascii_strcasecmp (flags
[i
], "pgp-sign") == 0) {
3750 action
= GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
));
3751 gtk_toggle_action_set_active (action
, TRUE
);
3752 } else if (g_ascii_strcasecmp (flags
[i
], "pgp-encrypt") == 0) {
3753 action
= GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
));
3754 gtk_toggle_action_set_active (action
, TRUE
);
3755 } else if (g_ascii_strcasecmp (flags
[i
], "smime-sign") == 0) {
3756 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
));
3757 gtk_toggle_action_set_active (action
, TRUE
);
3758 } else if (g_ascii_strcasecmp (flags
[i
], "smime-encrypt") == 0) {
3759 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
));
3760 gtk_toggle_action_set_active (action
, TRUE
);
3766 /* Remove any other X-Evolution-* headers that may have been set */
3767 xev
= emcu_remove_xevolution_headers (message
);
3768 camel_header_raw_clear (&xev
);
3770 /* Check for receipt request */
3771 if (camel_medium_get_header (
3772 CAMEL_MEDIUM (message
), "Disposition-Notification-To")) {
3773 action
= GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT
));
3774 gtk_toggle_action_set_active (action
, TRUE
);
3777 /* Check for mail priority */
3778 if (camel_medium_get_header (CAMEL_MEDIUM (message
), "X-Priority")) {
3779 action
= GTK_TOGGLE_ACTION (ACTION (PRIORITIZE_MESSAGE
));
3780 gtk_toggle_action_set_active (action
, TRUE
);
3783 /* set extra headers */
3784 headers
= CAMEL_MIME_PART (message
)->headers
;
3786 if (g_ascii_strcasecmp (headers
->name
, "References") == 0 ||
3787 g_ascii_strcasecmp (headers
->name
, "In-Reply-To") == 0) {
3789 composer
->priv
->extra_hdr_names
,
3790 g_strdup (headers
->name
));
3792 composer
->priv
->extra_hdr_values
,
3793 g_strdup (headers
->value
));
3796 headers
= headers
->next
;
3799 /* Restore the attachments and body text */
3800 content
= camel_medium_get_content (CAMEL_MEDIUM (message
));
3801 if (CAMEL_IS_MULTIPART (content
)) {
3802 CamelMimePart
*mime_part
;
3803 CamelMultipart
*multipart
;
3805 multipart
= CAMEL_MULTIPART (content
);
3806 mime_part
= CAMEL_MIME_PART (message
);
3807 content_type
= camel_mime_part_get_content_type (mime_part
);
3809 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3810 /* Handle the signed content and configure the
3811 * composer to sign outgoing messages. */
3812 handle_multipart_signed (
3813 composer
, multipart
, keep_signature
, cancellable
, 0);
3815 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3816 /* Decrypt the encrypted content and configure the
3817 * composer to encrypt outgoing messages. */
3818 handle_multipart_encrypted (
3819 composer
, mime_part
, keep_signature
, cancellable
, 0);
3821 } else if (camel_content_type_is (
3822 content_type
, "multipart", "alternative")) {
3823 /* This contains the text/plain and text/html
3824 * versions of the message body. */
3825 handle_multipart_alternative (
3826 composer
, multipart
, keep_signature
, cancellable
, 0);
3829 /* There must be attachments... */
3831 composer
, multipart
, keep_signature
, cancellable
, 0);
3834 CamelMimePart
*mime_part
;
3835 gboolean is_html
= FALSE
;
3839 mime_part
= CAMEL_MIME_PART (message
);
3840 content_type
= camel_mime_part_get_content_type (mime_part
);
3841 is_html
= camel_content_type_is (content_type
, "text", "html");
3843 if (content_type
!= NULL
&& (
3844 camel_content_type_is (
3845 content_type
, "application", "x-pkcs7-mime") ||
3846 camel_content_type_is (
3847 content_type
, "application", "pkcs7-mime"))) {
3849 gtk_toggle_action_set_active (
3851 ACTION (SMIME_ENCRYPT
)), TRUE
);
3854 /* If we are opening message from Drafts */
3855 if (is_message_from_draft
) {
3856 /* Extract the body */
3857 CamelDataWrapper
*dw
;
3859 dw
= camel_medium_get_content ((CamelMedium
*) mime_part
);
3861 CamelStream
*mem
= camel_stream_mem_new ();
3864 camel_data_wrapper_decode_to_stream_sync (dw
, mem
, cancellable
, NULL
);
3865 camel_stream_close (mem
, cancellable
, NULL
);
3867 bytes
= camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem
));
3868 if (bytes
&& bytes
->len
)
3869 html
= g_strndup ((const gchar
*) bytes
->data
, bytes
->len
);
3871 g_object_unref (mem
);
3875 html
= emcu_part_to_html (
3876 composer
, CAMEL_MIME_PART (message
),
3877 &length
, keep_signature
, cancellable
);
3879 e_msg_composer_set_pending_body (composer
, html
, length
, is_html
);
3882 e_html_editor_view_set_is_message_from_edit_as_new (view
, TRUE
);
3883 priv
->set_signature_from_message
= TRUE
;
3885 /* We wait until now to set the body text because we need to
3886 * ensure that the attachment bar has all the attachments before
3887 * we request them. */
3888 e_msg_composer_flush_pending_body (composer
);
3890 set_signature_gui (composer
);
3896 * e_msg_composer_new_redirect:
3897 * @shell: an #EShell
3898 * @message: The message to use as the source
3900 * Create a new message composer widget.
3902 * Returns: A pointer to the newly created widget
3905 e_msg_composer_new_redirect (EShell
*shell
,
3906 CamelMimeMessage
*message
,
3907 const gchar
*identity_uid
,
3908 GCancellable
*cancellable
)
3910 EMsgComposer
*composer
;
3911 EComposerHeaderTable
*table
;
3912 EHTMLEditor
*editor
;
3913 EHTMLEditorView
*view
;
3914 const gchar
*subject
;
3916 g_return_val_if_fail (E_IS_SHELL (shell
), NULL
);
3917 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), NULL
);
3919 composer
= e_msg_composer_new_with_message (
3920 shell
, message
, TRUE
, identity_uid
, cancellable
);
3921 table
= e_msg_composer_get_header_table (composer
);
3923 subject
= camel_mime_message_get_subject (message
);
3925 composer
->priv
->redirect
= message
;
3926 g_object_ref (message
);
3928 e_composer_header_table_set_subject (table
, subject
);
3930 editor
= e_msg_composer_get_editor (composer
);
3931 view
= e_html_editor_get_view (editor
);
3932 webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view
), FALSE
);
3938 * e_msg_composer_ref_session:
3939 * @composer: an #EMsgComposer
3941 * Returns the mail module's global #CamelSession instance. Calling
3942 * this function will load the mail module if it isn't already loaded.
3944 * The returned #CamelSession is referenced for thread-safety and must
3945 * be unreferenced with g_object_unref() when finished with it.
3947 * Returns: the mail module's #CamelSession
3950 e_msg_composer_ref_session (EMsgComposer
*composer
)
3953 EShellBackend
*shell_backend
;
3954 CamelSession
*session
= NULL
;
3956 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
3958 shell
= e_msg_composer_get_shell (composer
);
3959 shell_backend
= e_shell_get_backend_by_name (shell
, "mail");
3961 g_object_get (shell_backend
, "session", &session
, NULL
);
3962 g_return_val_if_fail (CAMEL_IS_SESSION (session
), NULL
);
3968 * e_msg_composer_get_shell:
3969 * @composer: an #EMsgComposer
3971 * Returns the #EShell that was passed to e_msg_composer_new().
3973 * Returns: the #EShell
3976 e_msg_composer_get_shell (EMsgComposer
*composer
)
3978 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
3980 return E_SHELL (composer
->priv
->shell
);
3984 msg_composer_send_cb (EMsgComposer
*composer
,
3985 GAsyncResult
*result
,
3986 AsyncContext
*context
)
3988 CamelMimeMessage
*message
;
3989 EAlertSink
*alert_sink
;
3990 EHTMLEditor
*editor
;
3991 EHTMLEditorView
*view
;
3992 GError
*error
= NULL
;
3994 alert_sink
= e_activity_get_alert_sink (context
->activity
);
3996 message
= e_msg_composer_get_message_finish (composer
, result
, &error
);
3998 if (e_activity_handle_cancellation (context
->activity
, error
)) {
3999 g_warn_if_fail (message
== NULL
);
4000 async_context_free (context
);
4001 g_error_free (error
);
4003 gtk_window_present (GTK_WINDOW (composer
));
4007 if (error
!= NULL
) {
4008 g_warn_if_fail (message
== NULL
);
4011 "mail-composer:no-build-message",
4012 error
->message
, NULL
);
4013 async_context_free (context
);
4014 g_error_free (error
);
4016 gtk_window_present (GTK_WINDOW (composer
));
4020 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
4022 /* The callback can set editor 'changed' if anything failed. */
4023 editor
= e_msg_composer_get_editor (composer
);
4024 view
= e_html_editor_get_view (editor
);
4025 e_html_editor_view_set_changed (view
, TRUE
);
4028 composer
, signals
[SEND
], 0,
4029 message
, context
->activity
);
4031 g_object_unref (message
);
4033 async_context_free (context
);
4037 * e_msg_composer_send:
4038 * @composer: an #EMsgComposer
4040 * Send the message in @composer.
4043 e_msg_composer_send (EMsgComposer
*composer
)
4045 EHTMLEditor
*editor
;
4046 AsyncContext
*context
;
4047 GCancellable
*cancellable
;
4048 gboolean proceed_with_send
= TRUE
;
4050 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4052 /* This gives the user a chance to abort the send. */
4053 g_signal_emit (composer
, signals
[PRESEND
], 0, &proceed_with_send
);
4055 if (!proceed_with_send
) {
4056 gtk_window_present (GTK_WINDOW (composer
));
4060 editor
= e_msg_composer_get_editor (composer
);
4062 context
= g_slice_new0 (AsyncContext
);
4063 context
->activity
= e_html_editor_new_activity (editor
);
4065 cancellable
= e_activity_get_cancellable (context
->activity
);
4067 e_msg_composer_get_message (
4068 composer
, G_PRIORITY_DEFAULT
, cancellable
,
4069 (GAsyncReadyCallback
) msg_composer_send_cb
,
4074 msg_composer_save_to_drafts_cb (EMsgComposer
*composer
,
4075 GAsyncResult
*result
,
4076 AsyncContext
*context
)
4078 CamelMimeMessage
*message
;
4079 EAlertSink
*alert_sink
;
4080 EHTMLEditor
*editor
;
4081 EHTMLEditorView
*view
;
4082 GError
*error
= NULL
;
4084 alert_sink
= e_activity_get_alert_sink (context
->activity
);
4086 message
= e_msg_composer_get_message_draft_finish (
4087 composer
, result
, &error
);
4089 if (e_activity_handle_cancellation (context
->activity
, error
)) {
4090 g_warn_if_fail (message
== NULL
);
4091 async_context_free (context
);
4092 g_error_free (error
);
4094 if (e_msg_composer_is_exiting (composer
)) {
4095 gtk_window_present (GTK_WINDOW (composer
));
4096 composer
->priv
->application_exiting
= FALSE
;
4102 if (error
!= NULL
) {
4103 g_warn_if_fail (message
== NULL
);
4106 "mail-composer:no-build-message",
4107 error
->message
, NULL
);
4108 async_context_free (context
);
4109 g_error_free (error
);
4111 if (e_msg_composer_is_exiting (composer
)) {
4112 gtk_window_present (GTK_WINDOW (composer
));
4113 composer
->priv
->application_exiting
= FALSE
;
4119 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
4121 /* The callback can set editor 'changed' if anything failed. */
4122 editor
= e_msg_composer_get_editor (composer
);
4123 view
= e_html_editor_get_view (editor
);
4124 e_html_editor_view_set_changed (view
, FALSE
);
4127 composer
, signals
[SAVE_TO_DRAFTS
],
4128 0, message
, context
->activity
);
4130 g_object_unref (message
);
4132 if (e_msg_composer_is_exiting (composer
))
4134 G_OBJECT (context
->activity
),
4135 (GWeakNotify
) gtk_widget_destroy
, composer
);
4137 async_context_free (context
);
4141 * e_msg_composer_save_to_drafts:
4142 * @composer: an #EMsgComposer
4144 * Save the message in @composer to the selected account's Drafts folder.
4147 e_msg_composer_save_to_drafts (EMsgComposer
*composer
)
4149 EHTMLEditor
*editor
;
4150 AsyncContext
*context
;
4151 GCancellable
*cancellable
;
4153 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4155 editor
= e_msg_composer_get_editor (composer
);
4157 context
= g_slice_new0 (AsyncContext
);
4158 context
->activity
= e_html_editor_new_activity (editor
);
4160 cancellable
= e_activity_get_cancellable (context
->activity
);
4162 e_msg_composer_get_message_draft (
4163 composer
, G_PRIORITY_DEFAULT
, cancellable
,
4164 (GAsyncReadyCallback
) msg_composer_save_to_drafts_cb
,
4169 msg_composer_save_to_outbox_cb (EMsgComposer
*composer
,
4170 GAsyncResult
*result
,
4171 AsyncContext
*context
)
4173 CamelMimeMessage
*message
;
4174 EAlertSink
*alert_sink
;
4175 EHTMLEditor
*editor
;
4176 EHTMLEditorView
*view
;
4177 GError
*error
= NULL
;
4179 alert_sink
= e_activity_get_alert_sink (context
->activity
);
4181 message
= e_msg_composer_get_message_finish (composer
, result
, &error
);
4183 if (e_activity_handle_cancellation (context
->activity
, error
)) {
4184 g_warn_if_fail (message
== NULL
);
4185 async_context_free (context
);
4186 g_error_free (error
);
4190 if (error
!= NULL
) {
4191 g_warn_if_fail (message
== NULL
);
4194 "mail-composer:no-build-message",
4195 error
->message
, NULL
);
4196 async_context_free (context
);
4197 g_error_free (error
);
4201 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
4204 composer
, signals
[SAVE_TO_OUTBOX
],
4205 0, message
, context
->activity
);
4207 g_object_unref (message
);
4209 async_context_free (context
);
4211 editor
= e_msg_composer_get_editor (composer
);
4212 view
= e_html_editor_get_view (editor
);
4213 e_html_editor_view_set_changed (view
, FALSE
);
4217 * e_msg_composer_save_to_outbox:
4218 * @composer: an #EMsgComposer
4220 * Save the message in @composer to the local Outbox folder.
4223 e_msg_composer_save_to_outbox (EMsgComposer
*composer
)
4225 EHTMLEditor
*editor
;
4226 AsyncContext
*context
;
4227 GCancellable
*cancellable
;
4228 gboolean proceed_with_save
= TRUE
;
4230 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4232 /* This gives the user a chance to abort the save. */
4233 g_signal_emit (composer
, signals
[PRESEND
], 0, &proceed_with_save
);
4235 if (!proceed_with_save
)
4238 editor
= e_msg_composer_get_editor (composer
);
4240 context
= g_slice_new0 (AsyncContext
);
4241 context
->activity
= e_html_editor_new_activity (editor
);
4243 cancellable
= e_activity_get_cancellable (context
->activity
);
4245 e_msg_composer_get_message (
4246 composer
, G_PRIORITY_DEFAULT
, cancellable
,
4247 (GAsyncReadyCallback
) msg_composer_save_to_outbox_cb
,
4252 msg_composer_print_cb (EMsgComposer
*composer
,
4253 GAsyncResult
*result
,
4254 AsyncContext
*context
)
4256 CamelMimeMessage
*message
;
4257 EAlertSink
*alert_sink
;
4258 GError
*error
= NULL
;
4260 alert_sink
= e_activity_get_alert_sink (context
->activity
);
4262 message
= e_msg_composer_get_message_print_finish (
4263 composer
, result
, &error
);
4265 if (e_activity_handle_cancellation (context
->activity
, error
)) {
4266 g_warn_if_fail (message
== NULL
);
4267 async_context_free (context
);
4268 g_error_free (error
);
4272 if (error
!= NULL
) {
4273 g_warn_if_fail (message
== NULL
);
4274 async_context_free (context
);
4277 "mail-composer:no-build-message",
4278 error
->message
, NULL
);
4279 g_error_free (error
);
4283 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
4286 composer
, signals
[PRINT
], 0,
4287 context
->print_action
, message
, context
->activity
);
4289 g_object_unref (message
);
4291 async_context_free (context
);
4295 * e_msg_composer_print:
4296 * @composer: an #EMsgComposer
4297 * @print_action: the print action to start
4299 * Print the message in @composer.
4302 e_msg_composer_print (EMsgComposer
*composer
,
4303 GtkPrintOperationAction print_action
)
4305 EHTMLEditor
*editor
;
4306 AsyncContext
*context
;
4307 GCancellable
*cancellable
;
4309 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4311 editor
= e_msg_composer_get_editor (composer
);
4313 context
= g_slice_new0 (AsyncContext
);
4314 context
->activity
= e_html_editor_new_activity (editor
);
4315 context
->print_action
= print_action
;
4317 cancellable
= e_activity_get_cancellable (context
->activity
);
4319 e_msg_composer_get_message_print (
4320 composer
, G_PRIORITY_DEFAULT
, cancellable
,
4321 (GAsyncReadyCallback
) msg_composer_print_cb
,
4326 add_recipients (GList
*list
,
4327 const gchar
*recips
)
4329 CamelInternetAddress
*cia
;
4330 const gchar
*name
, *addr
;
4333 cia
= camel_internet_address_new ();
4334 num
= camel_address_decode (CAMEL_ADDRESS (cia
), recips
);
4336 for (i
= 0; i
< num
; i
++) {
4337 if (camel_internet_address_get (cia
, i
, &name
, &addr
)) {
4338 EDestination
*dest
= e_destination_new ();
4339 e_destination_set_name (dest
, name
);
4340 e_destination_set_email (dest
, addr
);
4342 list
= g_list_append (list
, dest
);
4346 g_object_unref (cia
);
4352 list_contains_addr (const GList
*list
,
4355 g_return_val_if_fail (dest
!= NULL
, FALSE
);
4357 while (list
!= NULL
) {
4358 if (e_destination_equal (dest
, list
->data
))
4368 merge_cc_bcc (EDestination
**addrv
,
4376 for (ii
= 0; addrv
&& addrv
[ii
]; ii
++) {
4377 if (!list_contains_addr (to
, addrv
[ii
]) &&
4378 !list_contains_addr (cc
, addrv
[ii
]) &&
4379 !list_contains_addr (bcc
, addrv
[ii
])) {
4380 *merge_into
= g_list_append (
4381 *merge_into
, g_object_ref (addrv
[ii
]));
4387 merge_always_cc_and_bcc (EComposerHeaderTable
*table
,
4392 EDestination
**addrv
;
4394 g_return_if_fail (table
!= NULL
);
4395 g_return_if_fail (cc
!= NULL
);
4396 g_return_if_fail (bcc
!= NULL
);
4398 addrv
= e_composer_header_table_get_destinations_cc (table
);
4399 merge_cc_bcc (addrv
, cc
, to
, *cc
, *bcc
);
4400 e_destination_freev (addrv
);
4402 addrv
= e_composer_header_table_get_destinations_bcc (table
);
4403 merge_cc_bcc (addrv
, bcc
, to
, *cc
, *bcc
);
4404 e_destination_freev (addrv
);
4407 static const gchar
*blacklist
[] = { ".", "etc", ".." };
4410 file_is_blacklisted (const gchar
*argument
)
4413 gboolean blacklisted
= FALSE
;
4414 guint ii
, jj
, n_parts
;
4418 /* The "attach" argument may be a URI or local path. Normalize
4419 * it to a local path if we can. We only blacklist local files. */
4420 file
= g_file_new_for_commandline_arg (argument
);
4421 filename
= g_file_get_path (file
);
4422 g_object_unref (file
);
4424 if (filename
== NULL
)
4427 parts
= g_strsplit (filename
, G_DIR_SEPARATOR_S
, -1);
4428 n_parts
= g_strv_length (parts
);
4430 for (ii
= 0; ii
< G_N_ELEMENTS (blacklist
); ii
++) {
4431 for (jj
= 0; jj
< n_parts
; jj
++) {
4432 if (g_str_has_prefix (parts
[jj
], blacklist
[ii
])) {
4442 /* Don't blacklist files in trusted base directories. */
4443 if (g_str_has_prefix (filename
, g_get_user_data_dir ()))
4444 blacklisted
= FALSE
;
4445 if (g_str_has_prefix (filename
, g_get_user_cache_dir ()))
4446 blacklisted
= FALSE
;
4447 if (g_str_has_prefix (filename
, g_get_user_config_dir ()))
4448 blacklisted
= FALSE
;
4450 /* Apparently KDE still uses ~/.kde heavily, and some
4451 * distributions use ~/.kde4 to distinguish KDE4 data
4452 * from KDE3 data. Trust these directories as well. */
4454 base_dir
= g_build_filename (g_get_home_dir (), ".kde", NULL
);
4455 if (g_str_has_prefix (filename
, base_dir
))
4456 blacklisted
= FALSE
;
4459 base_dir
= g_build_filename (g_get_home_dir (), ".kde4", NULL
);
4460 if (g_str_has_prefix (filename
, base_dir
))
4461 blacklisted
= FALSE
;
4472 handle_mailto (EMsgComposer
*composer
,
4473 const gchar
*mailto
)
4475 EAttachmentView
*view
;
4476 EAttachmentStore
*store
;
4477 EComposerHeaderTable
*table
;
4478 GList
*to
= NULL
, *cc
= NULL
, *bcc
= NULL
;
4479 EDestination
**tov
, **ccv
, **bccv
;
4480 gchar
*subject
= NULL
, *body
= NULL
;
4481 gchar
*header
, *content
, *buf
;
4482 gsize nread
, nwritten
;
4486 table
= e_msg_composer_get_header_table (composer
);
4487 view
= e_msg_composer_get_attachment_view (composer
);
4488 store
= e_attachment_view_get_store (view
);
4490 buf
= g_strdup (mailto
);
4492 /* Parse recipients (everything after ':' until '?' or eos). */
4494 len
= strcspn (p
, "?");
4496 content
= g_strndup (p
, len
);
4497 camel_url_decode (content
);
4498 to
= add_recipients (to
, content
);
4507 len
= strcspn (p
, "=&");
4509 /* If it's malformed, give up. */
4513 header
= (gchar
*) p
;
4517 clen
= strcspn (p
, "&");
4519 content
= g_strndup (p
, clen
);
4521 if (!g_ascii_strcasecmp (header
, "to")) {
4522 camel_url_decode (content
);
4523 to
= add_recipients (to
, content
);
4524 } else if (!g_ascii_strcasecmp (header
, "cc")) {
4525 camel_url_decode (content
);
4526 cc
= add_recipients (cc
, content
);
4527 } else if (!g_ascii_strcasecmp (header
, "bcc")) {
4528 camel_url_decode (content
);
4529 bcc
= add_recipients (bcc
, content
);
4530 } else if (!g_ascii_strcasecmp (header
, "subject")) {
4532 camel_url_decode (content
);
4533 if (g_utf8_validate (content
, -1, NULL
)) {
4537 subject
= g_locale_to_utf8 (
4538 content
, clen
, &nread
,
4541 subject
= g_realloc (subject
, nwritten
+ 1);
4542 subject
[nwritten
] = '\0';
4545 } else if (!g_ascii_strcasecmp (header
, "body")) {
4547 camel_url_decode (content
);
4548 if (g_utf8_validate (content
, -1, NULL
)) {
4552 body
= g_locale_to_utf8 (
4553 content
, clen
, &nread
,
4556 body
= g_realloc (body
, nwritten
+ 1);
4557 body
[nwritten
] = '\0';
4560 } else if (!g_ascii_strcasecmp (header
, "attach") ||
4561 !g_ascii_strcasecmp (header
, "attachment")) {
4562 EAttachment
*attachment
;
4564 camel_url_decode (content
);
4565 if (file_is_blacklisted (content
))
4567 E_ALERT_SINK (e_msg_composer_get_editor (composer
)),
4568 "mail:blacklisted-file",
4570 if (g_ascii_strncasecmp (content
, "file:", 5) == 0)
4571 attachment
= e_attachment_new_for_uri (content
);
4573 attachment
= e_attachment_new_for_path (content
);
4574 e_attachment_store_add_attachment (store
, attachment
);
4575 e_attachment_load_async (
4576 attachment
, (GAsyncReadyCallback
)
4577 e_attachment_load_handle_error
, composer
);
4578 g_object_unref (attachment
);
4579 } else if (!g_ascii_strcasecmp (header
, "from")) {
4581 } else if (!g_ascii_strcasecmp (header
, "reply-to")) {
4584 /* add an arbitrary header? */
4585 camel_url_decode (content
);
4586 e_msg_composer_add_header (composer
, header
, content
);
4594 if (!g_ascii_strncasecmp (p
, "amp;", 4))
4602 merge_always_cc_and_bcc (table
, to
, &cc
, &bcc
);
4604 tov
= destination_list_to_vector (to
);
4605 ccv
= destination_list_to_vector (cc
);
4606 bccv
= destination_list_to_vector (bcc
);
4612 e_composer_header_table_set_destinations_to (table
, tov
);
4613 e_composer_header_table_set_destinations_cc (table
, ccv
);
4614 e_composer_header_table_set_destinations_bcc (table
, bccv
);
4616 e_destination_freev (tov
);
4617 e_destination_freev (ccv
);
4618 e_destination_freev (bccv
);
4620 e_composer_header_table_set_subject (table
, subject
);
4626 html_body
= camel_text_to_html (body
, CAMEL_MIME_FILTER_TOHTML_PRE
, 0);
4627 set_editor_text (composer
, html_body
, TRUE
, TRUE
);
4633 * e_msg_composer_new_from_url:
4634 * @shell: an #EShell
4635 * @url: a mailto URL
4637 * Create a new message composer widget, and fill in fields as
4638 * defined by the provided URL.
4641 e_msg_composer_new_from_url (EShell
*shell
,
4644 EMsgComposer
*composer
;
4646 g_return_val_if_fail (E_IS_SHELL (shell
), NULL
);
4647 g_return_val_if_fail (g_ascii_strncasecmp (url
, "mailto:", 7) == 0, NULL
);
4649 composer
= e_msg_composer_new (shell
);
4651 handle_mailto (composer
, url
);
4657 * e_msg_composer_set_body_text:
4658 * @composer: a composer object
4659 * @text: the HTML text to initialize the editor with
4660 * @update_signature: whether update signature in the text after setting it;
4661 * Might be usually called with TRUE.
4663 * Loads the given HTML text into the editor.
4666 e_msg_composer_set_body_text (EMsgComposer
*composer
,
4668 gboolean update_signature
)
4670 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4671 g_return_if_fail (text
!= NULL
);
4673 /* Every usage of e_msg_composer_set_body_text is called with HTML text */
4674 set_editor_text (composer
, text
, TRUE
, update_signature
);
4678 * e_msg_composer_set_body:
4679 * @composer: a composer object
4680 * @body: the data to initialize the composer with
4681 * @mime_type: the MIME type of data
4683 * Loads the given data into the composer as the message body.
4686 e_msg_composer_set_body (EMsgComposer
*composer
,
4688 const gchar
*mime_type
)
4690 EMsgComposerPrivate
*priv
= composer
->priv
;
4691 EComposerHeaderTable
*table
;
4692 EHTMLEditor
*editor
;
4693 EHTMLEditorView
*view
;
4695 const gchar
*identity_uid
;
4696 const gchar
*content
;
4698 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4700 editor
= e_msg_composer_get_editor (composer
);
4701 view
= e_html_editor_get_view (editor
);
4702 table
= e_msg_composer_get_header_table (composer
);
4704 /* Disable signature */
4705 priv
->disable_signature
= TRUE
;
4707 identity_uid
= e_composer_header_table_get_identity_uid (table
);
4708 source
= e_composer_header_table_ref_source (table
, identity_uid
);
4710 content
= _("The composer contains a non-text message body, which cannot be edited.");
4711 set_editor_text (composer
, content
, TRUE
, FALSE
);
4713 e_html_editor_view_set_html_mode (view
, FALSE
);
4714 webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view
), FALSE
);
4716 g_free (priv
->mime_body
);
4717 priv
->mime_body
= g_strdup (body
);
4718 g_free (priv
->mime_type
);
4719 priv
->mime_type
= g_strdup (mime_type
);
4721 if (g_ascii_strncasecmp (priv
->mime_type
, "text/calendar", 13) == 0) {
4722 ESourceMailComposition
*extension
;
4723 const gchar
*extension_name
;
4725 extension_name
= E_SOURCE_EXTENSION_MAIL_COMPOSITION
;
4726 extension
= e_source_get_extension (source
, extension_name
);
4728 if (!e_source_mail_composition_get_sign_imip (extension
)) {
4729 GtkToggleAction
*action
;
4731 action
= GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
));
4732 gtk_toggle_action_set_active (action
, FALSE
);
4734 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
));
4735 gtk_toggle_action_set_active (action
, FALSE
);
4739 g_object_unref (source
);
4743 * e_msg_composer_add_header:
4744 * @composer: an #EMsgComposer
4745 * @name: the header's name
4746 * @value: the header's value
4748 * Adds a new custom header created from @name and @value. The header
4749 * is not shown in the user interface but will be added to the resulting
4750 * MIME message when sending or saving.
4753 e_msg_composer_add_header (EMsgComposer
*composer
,
4757 EMsgComposerPrivate
*priv
;
4759 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4760 g_return_if_fail (name
!= NULL
);
4761 g_return_if_fail (value
!= NULL
);
4763 priv
= composer
->priv
;
4765 g_ptr_array_add (priv
->extra_hdr_names
, g_strdup (name
));
4766 g_ptr_array_add (priv
->extra_hdr_values
, g_strdup (value
));
4770 * e_msg_composer_set_header:
4771 * @composer: an #EMsgComposer
4772 * @name: the header's name
4773 * @value: the header's value
4775 * Replaces all custom headers matching @name that were added with
4776 * e_msg_composer_add_header() or e_msg_composer_set_header(), with
4777 * a new custom header created from @name and @value. The header is
4778 * not shown in the user interface but will be added to the resulting
4779 * MIME message when sending or saving.
4782 e_msg_composer_set_header (EMsgComposer
*composer
,
4786 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4787 g_return_if_fail (name
!= NULL
);
4788 g_return_if_fail (value
!= NULL
);
4790 e_msg_composer_remove_header (composer
, name
);
4791 e_msg_composer_add_header (composer
, name
, value
);
4795 * e_msg_composer_remove_header:
4796 * @composer: an #EMsgComposer
4797 * @name: the header's name
4799 * Removes all custom headers matching @name that were added with
4800 * e_msg_composer_add_header() or e_msg_composer_set_header().
4803 e_msg_composer_remove_header (EMsgComposer
*composer
,
4806 EMsgComposerPrivate
*priv
;
4809 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4810 g_return_if_fail (name
!= NULL
);
4812 priv
= composer
->priv
;
4814 for (ii
= 0; ii
< priv
->extra_hdr_names
->len
; ii
++) {
4815 if (g_strcmp0 (priv
->extra_hdr_names
->pdata
[ii
], name
) == 0) {
4816 g_free (priv
->extra_hdr_names
->pdata
[ii
]);
4817 g_free (priv
->extra_hdr_values
->pdata
[ii
]);
4818 g_ptr_array_remove_index (priv
->extra_hdr_names
, ii
);
4819 g_ptr_array_remove_index (priv
->extra_hdr_values
, ii
);
4825 * e_msg_composer_set_draft_headers:
4826 * @composer: an #EMsgComposer
4827 * @folder_uri: folder URI of the last saved draft
4828 * @message_uid: message UID of the last saved draft
4830 * Add special X-Evolution-Draft headers to remember the most recently
4831 * saved draft message, even across Evolution sessions. These headers
4832 * can be used to mark the draft message for deletion after saving a
4833 * newer draft or sending the composed message.
4836 e_msg_composer_set_draft_headers (EMsgComposer
*composer
,
4837 const gchar
*folder_uri
,
4838 const gchar
*message_uid
)
4840 const gchar
*header_name
;
4842 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4843 g_return_if_fail (folder_uri
!= NULL
);
4844 g_return_if_fail (message_uid
!= NULL
);
4846 header_name
= "X-Evolution-Draft-Folder";
4847 e_msg_composer_set_header (composer
, header_name
, folder_uri
);
4849 header_name
= "X-Evolution-Draft-Message";
4850 e_msg_composer_set_header (composer
, header_name
, message_uid
);
4854 * e_msg_composer_set_source_headers:
4855 * @composer: an #EMsgComposer
4856 * @folder_uri: folder URI of the source message
4857 * @message_uid: message UID of the source message
4858 * @flags: flags to set on the source message after sending
4860 * Add special X-Evolution-Source headers to remember the message being
4861 * forwarded or replied to, even across Evolution sessions. These headers
4862 * can be used to set appropriate flags on the source message after sending
4863 * the composed message.
4866 e_msg_composer_set_source_headers (EMsgComposer
*composer
,
4867 const gchar
*folder_uri
,
4868 const gchar
*message_uid
,
4869 CamelMessageFlags flags
)
4872 const gchar
*header_name
;
4874 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4875 g_return_if_fail (folder_uri
!= NULL
);
4876 g_return_if_fail (message_uid
!= NULL
);
4878 buffer
= g_string_sized_new (32);
4880 if (flags
& CAMEL_MESSAGE_ANSWERED
)
4881 g_string_append (buffer
, "ANSWERED ");
4882 if (flags
& CAMEL_MESSAGE_ANSWERED_ALL
)
4883 g_string_append (buffer
, "ANSWERED_ALL ");
4884 if (flags
& CAMEL_MESSAGE_FORWARDED
)
4885 g_string_append (buffer
, "FORWARDED ");
4886 if (flags
& CAMEL_MESSAGE_SEEN
)
4887 g_string_append (buffer
, "SEEN ");
4889 header_name
= "X-Evolution-Source-Folder";
4890 e_msg_composer_set_header (composer
, header_name
, folder_uri
);
4892 header_name
= "X-Evolution-Source-Message";
4893 e_msg_composer_set_header (composer
, header_name
, message_uid
);
4895 header_name
= "X-Evolution-Source-Flags";
4896 e_msg_composer_set_header (composer
, header_name
, buffer
->str
);
4898 g_string_free (buffer
, TRUE
);
4902 * e_msg_composer_attach:
4903 * @composer: a composer object
4904 * @mime_part: the #CamelMimePart to attach
4906 * Attaches @attachment to the message being composed in the composer.
4909 e_msg_composer_attach (EMsgComposer
*composer
,
4910 CamelMimePart
*mime_part
)
4912 EAttachmentView
*view
;
4913 EAttachmentStore
*store
;
4914 EAttachment
*attachment
;
4916 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4917 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part
));
4919 view
= e_msg_composer_get_attachment_view (composer
);
4920 store
= e_attachment_view_get_store (view
);
4922 attachment
= e_attachment_new ();
4923 e_attachment_set_mime_part (attachment
, mime_part
);
4924 e_attachment_store_add_attachment (store
, attachment
);
4925 e_attachment_load_async (
4926 attachment
, (GAsyncReadyCallback
)
4927 e_attachment_load_handle_error
, composer
);
4928 g_object_unref (attachment
);
4932 composer_get_message_ready (EMsgComposer
*composer
,
4933 GAsyncResult
*result
,
4934 GSimpleAsyncResult
*simple
)
4936 CamelMimeMessage
*message
;
4937 GError
*error
= NULL
;
4939 message
= composer_build_message_finish (composer
, result
, &error
);
4941 if (message
!= NULL
)
4942 g_simple_async_result_set_op_res_gpointer (
4943 simple
, message
, (GDestroyNotify
) g_object_unref
);
4945 if (error
!= NULL
) {
4946 g_warn_if_fail (message
== NULL
);
4947 g_simple_async_result_take_error (simple
, error
);
4950 g_simple_async_result_complete (simple
);
4952 g_object_unref (simple
);
4956 * e_msg_composer_get_message:
4957 * @composer: an #EMsgComposer
4959 * Retrieve the message edited by the user as a #CamelMimeMessage. The
4960 * #CamelMimeMessage object is created on the fly; subsequent calls to this
4961 * function will always create new objects from scratch.
4964 e_msg_composer_get_message (EMsgComposer
*composer
,
4966 GCancellable
*cancellable
,
4967 GAsyncReadyCallback callback
,
4970 GSimpleAsyncResult
*simple
;
4972 ComposerFlags flags
= 0;
4973 EHTMLEditor
*editor
;
4974 EHTMLEditorView
*view
;
4976 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4978 editor
= e_msg_composer_get_editor (composer
);
4979 view
= e_html_editor_get_view (editor
);
4981 simple
= g_simple_async_result_new (
4982 G_OBJECT (composer
), callback
,
4983 user_data
, e_msg_composer_get_message
);
4985 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
4987 if (e_html_editor_view_get_html_mode (view
))
4988 flags
|= COMPOSER_FLAG_HTML_CONTENT
;
4990 action
= ACTION (PRIORITIZE_MESSAGE
);
4991 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
4992 flags
|= COMPOSER_FLAG_PRIORITIZE_MESSAGE
;
4994 action
= ACTION (REQUEST_READ_RECEIPT
);
4995 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
4996 flags
|= COMPOSER_FLAG_REQUEST_READ_RECEIPT
;
4998 action
= ACTION (PGP_SIGN
);
4999 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5000 flags
|= COMPOSER_FLAG_PGP_SIGN
;
5002 action
= ACTION (PGP_ENCRYPT
);
5003 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5004 flags
|= COMPOSER_FLAG_PGP_ENCRYPT
;
5007 action
= ACTION (SMIME_SIGN
);
5008 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5009 flags
|= COMPOSER_FLAG_SMIME_SIGN
;
5011 action
= ACTION (SMIME_ENCRYPT
);
5012 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5013 flags
|= COMPOSER_FLAG_SMIME_ENCRYPT
;
5016 composer_build_message (
5017 composer
, flags
, io_priority
,
5018 cancellable
, (GAsyncReadyCallback
)
5019 composer_get_message_ready
, simple
);
5023 e_msg_composer_get_message_finish (EMsgComposer
*composer
,
5024 GAsyncResult
*result
,
5027 GSimpleAsyncResult
*simple
;
5028 CamelMimeMessage
*message
;
5030 g_return_val_if_fail (
5031 g_simple_async_result_is_valid (
5032 result
, G_OBJECT (composer
),
5033 e_msg_composer_get_message
), NULL
);
5035 simple
= G_SIMPLE_ASYNC_RESULT (result
);
5036 message
= g_simple_async_result_get_op_res_gpointer (simple
);
5038 if (g_simple_async_result_propagate_error (simple
, error
))
5041 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), NULL
);
5043 return g_object_ref (message
);
5047 e_msg_composer_get_message_print (EMsgComposer
*composer
,
5049 GCancellable
*cancellable
,
5050 GAsyncReadyCallback callback
,
5053 GSimpleAsyncResult
*simple
;
5054 ComposerFlags flags
= 0;
5056 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5058 simple
= g_simple_async_result_new (
5059 G_OBJECT (composer
), callback
,
5060 user_data
, e_msg_composer_get_message_print
);
5062 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
5064 flags
|= COMPOSER_FLAG_HTML_CONTENT
;
5065 flags
|= COMPOSER_FLAG_SAVE_OBJECT_DATA
;
5067 composer_build_message (
5068 composer
, flags
, io_priority
,
5069 cancellable
, (GAsyncReadyCallback
)
5070 composer_get_message_ready
, simple
);
5074 e_msg_composer_get_message_print_finish (EMsgComposer
*composer
,
5075 GAsyncResult
*result
,
5078 GSimpleAsyncResult
*simple
;
5079 CamelMimeMessage
*message
;
5081 g_return_val_if_fail (
5082 g_simple_async_result_is_valid (
5083 result
, G_OBJECT (composer
),
5084 e_msg_composer_get_message_print
), NULL
);
5086 simple
= G_SIMPLE_ASYNC_RESULT (result
);
5087 message
= g_simple_async_result_get_op_res_gpointer (simple
);
5089 if (g_simple_async_result_propagate_error (simple
, error
))
5092 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), NULL
);
5094 return g_object_ref (message
);
5098 e_msg_composer_get_message_draft (EMsgComposer
*composer
,
5100 GCancellable
*cancellable
,
5101 GAsyncReadyCallback callback
,
5104 EHTMLEditor
*editor
;
5105 EHTMLEditorView
*view
;
5106 GSimpleAsyncResult
*simple
;
5107 ComposerFlags flags
= COMPOSER_FLAG_SAVE_DRAFT
;
5110 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5112 simple
= g_simple_async_result_new (
5113 G_OBJECT (composer
), callback
,
5114 user_data
, e_msg_composer_get_message_draft
);
5116 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
5118 editor
= e_msg_composer_get_editor (composer
);
5119 view
= e_html_editor_get_view (editor
);
5120 /* We need to remember composer mode */
5121 if (e_html_editor_view_get_html_mode (view
))
5122 flags
|= COMPOSER_FLAG_HTML_MODE
;
5123 /* We want to save HTML content everytime when we save as draft */
5124 flags
|= COMPOSER_FLAG_SAVE_DRAFT
;
5126 action
= ACTION (PRIORITIZE_MESSAGE
);
5127 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5128 flags
|= COMPOSER_FLAG_PRIORITIZE_MESSAGE
;
5130 action
= ACTION (REQUEST_READ_RECEIPT
);
5131 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5132 flags
|= COMPOSER_FLAG_REQUEST_READ_RECEIPT
;
5134 action
= ACTION (PGP_SIGN
);
5135 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5136 flags
|= COMPOSER_FLAG_PGP_SIGN
;
5138 action
= ACTION (PGP_ENCRYPT
);
5139 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5140 flags
|= COMPOSER_FLAG_PGP_ENCRYPT
;
5143 action
= ACTION (SMIME_SIGN
);
5144 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5145 flags
|= COMPOSER_FLAG_SMIME_SIGN
;
5147 action
= ACTION (SMIME_ENCRYPT
);
5148 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5149 flags
|= COMPOSER_FLAG_SMIME_ENCRYPT
;
5152 composer_build_message (
5153 composer
, flags
, io_priority
,
5154 cancellable
, (GAsyncReadyCallback
)
5155 composer_get_message_ready
, simple
);
5159 e_msg_composer_get_message_draft_finish (EMsgComposer
*composer
,
5160 GAsyncResult
*result
,
5163 GSimpleAsyncResult
*simple
;
5164 CamelMimeMessage
*message
;
5166 g_return_val_if_fail (
5167 g_simple_async_result_is_valid (
5168 result
, G_OBJECT (composer
),
5169 e_msg_composer_get_message_draft
), NULL
);
5171 simple
= G_SIMPLE_ASYNC_RESULT (result
);
5172 message
= g_simple_async_result_get_op_res_gpointer (simple
);
5174 if (g_simple_async_result_propagate_error (simple
, error
))
5177 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), NULL
);
5179 return g_object_ref (message
);
5182 CamelInternetAddress
*
5183 e_msg_composer_get_from (EMsgComposer
*composer
)
5185 CamelInternetAddress
*inet_address
= NULL
;
5186 ESourceMailIdentity
*mail_identity
;
5187 EComposerHeaderTable
*table
;
5189 const gchar
*extension_name
;
5194 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5196 table
= e_msg_composer_get_header_table (composer
);
5198 uid
= e_composer_header_table_get_identity_uid (table
);
5199 source
= e_composer_header_table_ref_source (table
, uid
);
5200 g_return_val_if_fail (source
!= NULL
, NULL
);
5202 extension_name
= E_SOURCE_EXTENSION_MAIL_IDENTITY
;
5203 mail_identity
= e_source_get_extension (source
, extension_name
);
5205 name
= e_source_mail_identity_dup_name (mail_identity
);
5206 address
= e_source_mail_identity_dup_address (mail_identity
);
5208 g_object_unref (source
);
5210 if (name
!= NULL
&& address
!= NULL
) {
5211 inet_address
= camel_internet_address_new ();
5212 camel_internet_address_add (inet_address
, name
, address
);
5218 return inet_address
;
5221 CamelInternetAddress
*
5222 e_msg_composer_get_reply_to (EMsgComposer
*composer
)
5224 CamelInternetAddress
*address
;
5225 EComposerHeaderTable
*table
;
5226 const gchar
*reply_to
;
5228 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5230 table
= e_msg_composer_get_header_table (composer
);
5232 reply_to
= e_composer_header_table_get_reply_to (table
);
5233 if (reply_to
== NULL
|| *reply_to
== '\0')
5236 address
= camel_internet_address_new ();
5237 if (camel_address_unformat (CAMEL_ADDRESS (address
), reply_to
) == -1) {
5238 g_object_unref (address
);
5246 * e_msg_composer_get_raw_message_text_without_signature:
5248 * Returns the text/plain of the message from composer without signature
5251 e_msg_composer_get_raw_message_text_without_signature (EMsgComposer
*composer
)
5253 EHTMLEditor
*editor
;
5254 EHTMLEditorView
*view
;
5257 WebKitDOMDocument
*document
;
5258 WebKitDOMNodeList
*list
;
5260 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5262 editor
= e_msg_composer_get_editor (composer
);
5263 view
= e_html_editor_get_view (editor
);
5264 document
= webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view
));
5265 array
= g_byte_array_new ();
5267 list
= webkit_dom_document_query_selector_all (
5268 document
, "body > *:not(.-x-evo-signature-wrapper)", NULL
);
5269 length
= webkit_dom_node_list_get_length (list
);
5270 for (ii
= 0; ii
< length
; ii
++) {
5272 WebKitDOMNode
*node
= webkit_dom_node_list_item (list
, ii
);
5274 text
= webkit_dom_html_element_get_inner_text (
5275 WEBKIT_DOM_HTML_ELEMENT (node
));
5276 g_byte_array_append (array
, (guint8
*) text
, strlen (text
));
5278 g_object_unref (node
);
5281 g_object_unref (list
);
5287 * e_msg_composer_get_raw_message_text:
5289 * Returns the text/plain of the message from composer
5292 e_msg_composer_get_raw_message_text (EMsgComposer
*composer
)
5294 EHTMLEditor
*editor
;
5295 EHTMLEditorView
*view
;
5298 WebKitDOMDocument
*document
;
5299 WebKitDOMHTMLElement
*body
;
5301 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5303 editor
= e_msg_composer_get_editor (composer
);
5304 view
= e_html_editor_get_view (editor
);
5305 document
= webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view
));
5306 body
= webkit_dom_document_get_body (document
);
5308 array
= g_byte_array_new ();
5309 text
= webkit_dom_html_element_get_inner_text (body
);
5310 g_byte_array_append (array
, (guint8
*) text
, strlen (text
));
5317 e_msg_composer_is_exiting (EMsgComposer
*composer
)
5319 g_return_val_if_fail (composer
!= NULL
, FALSE
);
5321 return composer
->priv
->application_exiting
;
5325 e_msg_composer_request_close (EMsgComposer
*composer
)
5327 g_return_if_fail (composer
!= NULL
);
5329 composer
->priv
->application_exiting
= TRUE
;
5332 /* Returns whether can close the composer immediately. It will return FALSE
5333 * also when saving to drafts, but the e_msg_composer_is_exiting will return
5334 * TRUE for this case. can_save_draft means whether can save draft
5335 * immediately, or rather keep it on the caller (when FALSE). If kept on the
5336 * folder, then returns FALSE and sets interval variable to return TRUE in
5337 * e_msg_composer_is_exiting. */
5339 e_msg_composer_can_close (EMsgComposer
*composer
,
5340 gboolean can_save_draft
)
5342 gboolean res
= FALSE
;
5343 EHTMLEditor
*editor
;
5344 EHTMLEditorView
*view
;
5345 EComposerHeaderTable
*table
;
5348 const gchar
*subject
, *message_name
;
5351 widget
= GTK_WIDGET (composer
);
5352 editor
= e_msg_composer_get_editor (composer
);
5353 view
= e_html_editor_get_view (editor
);
5355 /* this means that there is an async operation running,
5356 * in which case the composer cannot be closed */
5357 if (!gtk_action_group_get_sensitive (composer
->priv
->async_actions
))
5360 if (!e_html_editor_view_get_changed (view
))
5363 window
= gtk_widget_get_window (widget
);
5364 gdk_window_raise (window
);
5366 table
= e_msg_composer_get_header_table (composer
);
5367 subject
= e_composer_header_table_get_subject (table
);
5369 if (subject
== NULL
|| *subject
== '\0')
5370 message_name
= "mail-composer:exit-unsaved-no-subject";
5372 message_name
= "mail-composer:exit-unsaved";
5374 response
= e_alert_run_dialog_for_args (
5375 GTK_WINDOW (composer
),
5380 case GTK_RESPONSE_YES
:
5381 gtk_widget_hide (widget
);
5382 e_msg_composer_request_close (composer
);
5384 gtk_action_activate (ACTION (SAVE_DRAFT
));
5387 case GTK_RESPONSE_NO
:
5391 case GTK_RESPONSE_CANCEL
:
5398 EComposerHeaderTable
*
5399 e_msg_composer_get_header_table (EMsgComposer
*composer
)
5401 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5403 return E_COMPOSER_HEADER_TABLE (composer
->priv
->header_table
);
5407 e_msg_composer_get_attachment_view (EMsgComposer
*composer
)
5409 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5411 return E_ATTACHMENT_VIEW (composer
->priv
->attachment_paned
);
5415 e_save_spell_languages (const GList
*spell_dicts
)
5417 GSettings
*settings
;
5418 GPtrArray
*lang_array
;
5420 /* Build a list of spell check language codes. */
5421 lang_array
= g_ptr_array_new ();
5422 while (spell_dicts
!= NULL
) {
5423 ESpellDictionary
*dict
= spell_dicts
->data
;
5424 const gchar
*language_code
;
5426 language_code
= e_spell_dictionary_get_code (dict
);
5427 g_ptr_array_add (lang_array
, (gpointer
) language_code
);
5429 spell_dicts
= g_list_next (spell_dicts
);
5432 g_ptr_array_add (lang_array
, NULL
);
5434 /* Save the language codes to GSettings. */
5435 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
5436 g_settings_set_strv (
5437 settings
, "composer-spell-languages",
5438 (const gchar
* const *) lang_array
->pdata
);
5439 g_object_unref (settings
);
5441 g_ptr_array_free (lang_array
, TRUE
);
5445 e_msg_composer_is_from_new_message (EMsgComposer
*composer
,
5446 gboolean is_from_new_message
)
5448 g_return_if_fail (composer
!= NULL
);
5450 composer
->priv
->is_from_new_message
= is_from_new_message
;
5454 e_msg_composer_save_focused_widget (EMsgComposer
*composer
)
5458 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5460 widget
= gtk_window_get_focus (GTK_WINDOW (composer
));
5461 composer
->priv
->focused_entry
= widget
;
5463 if (E_IS_HTML_EDITOR_VIEW (widget
)) {
5464 EHTMLEditorSelection
*selection
;
5466 selection
= e_html_editor_view_get_selection (
5467 E_HTML_EDITOR_VIEW (widget
));
5469 e_html_editor_selection_save (selection
);
5472 if (GTK_IS_EDITABLE (widget
)) {
5473 gtk_editable_get_selection_bounds (
5474 GTK_EDITABLE (widget
),
5475 &composer
->priv
->focused_entry_selection_start
,
5476 &composer
->priv
->focused_entry_selection_end
);
5481 e_msg_composer_restore_focus_on_composer (EMsgComposer
*composer
)
5483 GtkWidget
*widget
= composer
->priv
->focused_entry
;
5485 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5490 gtk_window_set_focus (GTK_WINDOW (composer
), widget
);
5492 if (GTK_IS_EDITABLE (widget
)) {
5493 gtk_editable_select_region (
5494 GTK_EDITABLE (widget
),
5495 composer
->priv
->focused_entry_selection_start
,
5496 composer
->priv
->focused_entry_selection_end
);
5499 if (E_IS_HTML_EDITOR_VIEW (widget
)) {
5500 EHTMLEditorSelection
*selection
;
5502 e_html_editor_view_force_spell_check_in_viewport (E_HTML_EDITOR_VIEW (widget
));
5504 selection
= e_html_editor_view_get_selection (
5505 E_HTML_EDITOR_VIEW (widget
));
5507 e_html_editor_selection_restore (selection
);
5510 composer
->priv
->focused_entry
= NULL
;