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)
26 #include "evolution-config.h"
30 #include <sys/types.h>
38 #include "e-composer-from-header.h"
39 #include "e-composer-text-header.h"
40 #include "e-composer-private.h"
42 #include <em-format/e-mail-part.h>
43 #include <em-format/e-mail-parser.h>
44 #include <em-format/e-mail-formatter-quote.h>
46 #include <shell/e-shell.h>
48 #include <libemail-engine/libemail-engine.h>
50 typedef struct _AsyncContext AsyncContext
;
52 struct _AsyncContext
{
55 CamelMimeMessage
*message
;
56 CamelDataWrapper
*top_level_part
;
57 CamelDataWrapper
*text_plain_part
;
60 CamelSession
*session
;
61 CamelInternetAddress
*from
;
63 CamelTransferEncoding plain_encoding
;
64 GtkPrintOperationAction print_action
;
66 GPtrArray
*recipients
;
68 guint skip_content
: 1;
69 guint need_thread
: 1;
71 guint pgp_encrypt
: 1;
73 guint smime_encrypt
: 1;
76 /* Flags for building a message. */
78 COMPOSER_FLAG_HTML_CONTENT
= 1 << 0,
79 COMPOSER_FLAG_SAVE_OBJECT_DATA
= 1 << 1,
80 COMPOSER_FLAG_PRIORITIZE_MESSAGE
= 1 << 2,
81 COMPOSER_FLAG_REQUEST_READ_RECEIPT
= 1 << 3,
82 COMPOSER_FLAG_PGP_SIGN
= 1 << 4,
83 COMPOSER_FLAG_PGP_ENCRYPT
= 1 << 5,
84 COMPOSER_FLAG_SMIME_SIGN
= 1 << 6,
85 COMPOSER_FLAG_SMIME_ENCRYPT
= 1 << 7,
86 COMPOSER_FLAG_HTML_MODE
= 1 << 8,
87 COMPOSER_FLAG_SAVE_DRAFT
= 1 << 9
96 PROP_IS_REPLY_OR_FORWARD
109 static GtkTargetEntry drag_dest_targets
[] = {
110 { (gchar
*) "text/uri-list", 0, E_DND_TARGET_TYPE_TEXT_URI_LIST
},
111 { (gchar
*) "_NETSCAPE_URL", 0, E_DND_TARGET_TYPE_MOZILLA_URL
},
112 { (gchar
*) "text/html", 0, E_DND_TARGET_TYPE_TEXT_HTML
},
113 { (gchar
*) "UTF8_STRING", 0, E_DND_TARGET_TYPE_UTF8_STRING
},
114 { (gchar
*) "text/plain", 0, E_DND_TARGET_TYPE_TEXT_PLAIN
},
115 { (gchar
*) "STRING", 0, E_DND_TARGET_TYPE_STRING
},
116 { (gchar
*) "text/plain;charset=utf-8", 0, E_DND_TARGET_TYPE_TEXT_PLAIN_UTF8
},
119 static guint signals
[LAST_SIGNAL
];
121 /* used by e_msg_composer_add_message_attachments () */
122 static void add_attachments_from_multipart (EMsgComposer
*composer
,
123 CamelMultipart
*multipart
,
124 gboolean just_inlines
,
127 /* used by e_msg_composer_setup_with_message () */
128 static void handle_multipart (EMsgComposer
*composer
,
129 CamelMultipart
*multipart
,
130 gboolean keep_signature
,
131 GCancellable
*cancellable
,
133 static void handle_multipart_alternative (EMsgComposer
*composer
,
134 CamelMultipart
*multipart
,
135 gboolean keep_signature
,
136 GCancellable
*cancellable
,
138 static void handle_multipart_encrypted (EMsgComposer
*composer
,
139 CamelMimePart
*multipart
,
140 gboolean keep_signature
,
141 GCancellable
*cancellable
,
143 static void handle_multipart_signed (EMsgComposer
*composer
,
144 CamelMultipart
*multipart
,
145 gboolean keep_signature
,
146 GCancellable
*cancellable
,
149 G_DEFINE_TYPE_WITH_CODE (
153 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE
, NULL
))
156 async_context_free (AsyncContext
*context
)
158 g_clear_object (&context
->activity
);
159 g_clear_object (&context
->message
);
160 g_clear_object (&context
->top_level_part
);
161 g_clear_object (&context
->text_plain_part
);
162 g_clear_object (&context
->source
);
163 g_clear_object (&context
->session
);
164 g_clear_object (&context
->from
);
166 if (context
->recipients
!= NULL
)
167 g_ptr_array_free (context
->recipients
, TRUE
);
169 g_slice_free (AsyncContext
, context
);
176 * Converts a mime part's contents into html text. If @credits is given,
177 * then it will be used as an attribution string, and the
178 * content will be cited. Otherwise no citation or attribution
181 * Return Value: The part in displayable html format.
184 emcu_part_to_html (EMsgComposer
*composer
,
187 gboolean keep_signature
,
188 GCancellable
*cancellable
)
190 CamelSession
*session
;
191 GOutputStream
*stream
;
194 EMailFormatter
*formatter
;
195 EMailPartList
*part_list
;
199 gsize n_bytes_written
= 0;
200 GQueue queue
= G_QUEUE_INIT
;
202 shell
= e_shell_get_default ();
203 window
= e_shell_get_active_window (shell
);
205 session
= e_msg_composer_ref_session (composer
);
207 part_list
= e_mail_part_list_new (NULL
, NULL
, NULL
);
209 part_id
= g_string_sized_new (0);
210 parser
= e_mail_parser_new (session
);
211 e_mail_parser_parse_part (
212 parser
, part
, part_id
, cancellable
, &queue
);
213 while (!g_queue_is_empty (&queue
)) {
214 EMailPart
*mail_part
= g_queue_pop_head (&queue
);
216 if (!e_mail_part_get_is_attachment (mail_part
) &&
217 !mail_part
->is_hidden
)
218 e_mail_part_list_add_part (part_list
, mail_part
);
220 g_object_unref (mail_part
);
222 g_string_free (part_id
, TRUE
);
223 g_object_unref (parser
);
224 g_object_unref (session
);
226 if (e_mail_part_list_is_empty (part_list
)) {
227 g_object_unref (part_list
);
231 stream
= g_memory_output_stream_new_resizable ();
233 formatter
= e_mail_formatter_quote_new (
234 NULL
, keep_signature
? E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG
: 0);
235 e_mail_formatter_update_style (
237 gtk_widget_get_state_flags (GTK_WIDGET (window
)));
239 e_mail_formatter_format_sync (
240 formatter
, part_list
, stream
,
241 0, E_MAIL_FORMATTER_MODE_PRINTING
, cancellable
);
243 g_object_unref (formatter
);
244 g_object_unref (part_list
);
246 g_output_stream_write_all (stream
, "", 1, &n_bytes_written
, NULL
, NULL
);
248 g_output_stream_close (stream
, NULL
, NULL
);
250 text
= g_memory_output_stream_steal_data (
251 G_MEMORY_OUTPUT_STREAM (stream
));
254 *len
= strlen (text
);
256 g_object_unref (stream
);
261 static EDestination
**
262 destination_list_to_vector_sized (GList
*list
,
265 EDestination
**destv
;
269 n
= g_list_length (list
);
274 destv
= g_new (EDestination
*, n
+ 1);
275 while (list
!= NULL
&& i
< n
) {
276 destv
[i
] = E_DESTINATION (list
->data
);
279 list
= g_list_next (list
);
286 static EDestination
**
287 destination_list_to_vector (GList
*list
)
289 return destination_list_to_vector_sized (list
, -1);
295 text_requires_quoted_printable (const gchar
*text
,
307 if (len
>= 5 && strncmp (text
, "From ", 5) == 0)
310 for (p
= text
, pos
= 0; pos
+ 6 <= len
; pos
++, p
++) {
311 if (*p
== '\n' && strncmp (p
+ 1, "From ", 5) == 0)
319 best_encoding (GByteArray
*buf
,
320 const gchar
*charset
,
321 CamelTransferEncoding
*encoding
)
323 gchar
*in
, *out
, outbuf
[256], *ch
;
325 gint status
, count
= 0;
331 cd
= camel_iconv_open (charset
, "utf-8");
332 if (cd
== (iconv_t
) -1)
335 in
= (gchar
*) buf
->data
;
339 outlen
= sizeof (outbuf
);
340 status
= camel_iconv (cd
, (const gchar
**) &in
, &inlen
, &out
, &outlen
);
341 for (ch
= out
- 1; ch
>= outbuf
; ch
--) {
342 if ((guchar
) *ch
> 127)
345 } while (status
== (gsize
) -1 && errno
== E2BIG
);
346 camel_iconv_close (cd
);
348 if (status
== (gsize
) -1 || status
> 0)
351 if ((count
== 0) && (buf
->len
< LINE_LEN
) &&
352 !text_requires_quoted_printable (
353 (const gchar
*) buf
->data
, buf
->len
))
354 *encoding
= CAMEL_TRANSFER_ENCODING_7BIT
;
355 else if (count
<= buf
->len
* 0.17)
356 *encoding
= CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
;
358 *encoding
= CAMEL_TRANSFER_ENCODING_BASE64
;
364 best_charset (GByteArray
*buf
,
365 const gchar
*default_charset
,
366 CamelTransferEncoding
*encoding
)
368 const gchar
*charset
;
370 /* First try US-ASCII */
371 if (best_encoding (buf
, "US-ASCII", encoding
) &&
372 *encoding
== CAMEL_TRANSFER_ENCODING_7BIT
)
375 /* Next try the user-specified charset for this message */
376 if (best_encoding (buf
, default_charset
, encoding
))
377 return g_strdup (default_charset
);
379 /* Now try the user's default charset from the mail config */
380 charset
= e_composer_get_default_charset ();
381 if (best_encoding (buf
, charset
, encoding
))
382 return g_strdup (charset
);
384 /* Try to find something that will work */
385 charset
= camel_charset_best (
386 (const gchar
*) buf
->data
, buf
->len
);
387 if (charset
== NULL
) {
388 *encoding
= CAMEL_TRANSFER_ENCODING_7BIT
;
392 if (!best_encoding (buf
, charset
, encoding
))
393 *encoding
= CAMEL_TRANSFER_ENCODING_BASE64
;
395 return g_strdup (charset
);
398 /* These functions builds a CamelMimeMessage for the message that the user has
399 * composed in 'composer'.
403 set_recipients_from_destv (CamelMimeMessage
*msg
,
404 EDestination
**to_destv
,
405 EDestination
**cc_destv
,
406 EDestination
**bcc_destv
,
409 CamelInternetAddress
*to_addr
;
410 CamelInternetAddress
*cc_addr
;
411 CamelInternetAddress
*bcc_addr
;
412 CamelInternetAddress
*target
;
413 const gchar
*text_addr
, *header
;
414 gboolean seen_hidden_list
= FALSE
;
417 to_addr
= camel_internet_address_new ();
418 cc_addr
= camel_internet_address_new ();
419 bcc_addr
= camel_internet_address_new ();
421 for (i
= 0; to_destv
!= NULL
&& to_destv
[i
] != NULL
; ++i
) {
422 text_addr
= e_destination_get_address (to_destv
[i
]);
424 if (text_addr
&& *text_addr
) {
426 if (e_destination_is_evolution_list (to_destv
[i
])
427 && !e_destination_list_show_addresses (to_destv
[i
])) {
429 seen_hidden_list
= TRUE
;
432 if (camel_address_decode (CAMEL_ADDRESS (target
), text_addr
) <= 0)
433 camel_internet_address_add (target
, "", text_addr
);
437 for (i
= 0; cc_destv
!= NULL
&& cc_destv
[i
] != NULL
; ++i
) {
438 text_addr
= e_destination_get_address (cc_destv
[i
]);
439 if (text_addr
&& *text_addr
) {
441 if (e_destination_is_evolution_list (cc_destv
[i
])
442 && !e_destination_list_show_addresses (cc_destv
[i
])) {
444 seen_hidden_list
= TRUE
;
447 if (camel_address_decode (CAMEL_ADDRESS (target
), text_addr
) <= 0)
448 camel_internet_address_add (target
, "", text_addr
);
452 for (i
= 0; bcc_destv
!= NULL
&& bcc_destv
[i
] != NULL
; ++i
) {
453 text_addr
= e_destination_get_address (bcc_destv
[i
]);
454 if (text_addr
&& *text_addr
) {
455 if (camel_address_decode (CAMEL_ADDRESS (bcc_addr
), text_addr
) <= 0)
456 camel_internet_address_add (bcc_addr
, "", text_addr
);
461 header
= CAMEL_RECIPIENT_TYPE_RESENT_TO
;
463 header
= CAMEL_RECIPIENT_TYPE_TO
;
465 if (camel_address_length (CAMEL_ADDRESS (to_addr
)) > 0) {
466 camel_mime_message_set_recipients (msg
, header
, to_addr
);
467 } else if (seen_hidden_list
) {
468 camel_medium_set_header (
469 CAMEL_MEDIUM (msg
), header
, "Undisclosed-Recipient:;");
472 header
= redirect
? CAMEL_RECIPIENT_TYPE_RESENT_CC
: CAMEL_RECIPIENT_TYPE_CC
;
473 if (camel_address_length (CAMEL_ADDRESS (cc_addr
)) > 0) {
474 camel_mime_message_set_recipients (msg
, header
, cc_addr
);
477 header
= redirect
? CAMEL_RECIPIENT_TYPE_RESENT_BCC
: CAMEL_RECIPIENT_TYPE_BCC
;
478 if (camel_address_length (CAMEL_ADDRESS (bcc_addr
)) > 0) {
479 camel_mime_message_set_recipients (msg
, header
, bcc_addr
);
482 g_object_unref (to_addr
);
483 g_object_unref (cc_addr
);
484 g_object_unref (bcc_addr
);
488 build_message_headers (EMsgComposer
*composer
,
489 CamelMimeMessage
*message
,
492 EComposerHeaderTable
*table
;
493 EComposerHeader
*header
;
495 gchar
*alias_name
= NULL
, *alias_address
= NULL
, *uid
;
496 const gchar
*subject
;
497 const gchar
*reply_to
;
499 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
500 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
502 table
= e_msg_composer_get_header_table (composer
);
504 uid
= e_composer_header_table_dup_identity_uid (table
, &alias_name
, &alias_address
);
506 source
= e_composer_header_table_ref_source (table
, uid
);
511 subject
= e_composer_header_table_get_subject (table
);
512 camel_mime_message_set_subject (message
, subject
);
514 if (source
!= NULL
) {
516 CamelInternetAddress
*addr
;
517 ESourceMailSubmission
*ms
;
518 EComposerHeader
*composer_header
;
519 const gchar
*extension_name
;
520 const gchar
*header_name
;
521 const gchar
*name
= NULL
, *address
= NULL
;
522 const gchar
*transport_uid
;
523 const gchar
*sent_folder
= NULL
;
524 gboolean is_from_override
= FALSE
;
526 composer_header
= e_composer_header_table_get_header (table
, E_COMPOSER_HEADER_FROM
);
527 if (e_composer_from_header_get_override_visible (E_COMPOSER_FROM_HEADER (composer_header
))) {
528 name
= e_composer_header_table_get_from_name (table
);
529 address
= e_composer_header_table_get_from_address (table
);
531 if (address
&& !*address
) {
536 is_from_override
= address
!= NULL
;
543 address
= alias_address
;
546 if (!is_from_override
&& (!address
|| !name
|| !*name
)) {
547 ESourceMailIdentity
*mail_identity
;
549 mail_identity
= e_source_get_extension (source
, E_SOURCE_EXTENSION_MAIL_IDENTITY
);
552 name
= e_source_mail_identity_get_name (mail_identity
);
555 address
= e_source_mail_identity_get_address (mail_identity
);
558 extension_name
= E_SOURCE_EXTENSION_MAIL_SUBMISSION
;
559 ms
= e_source_get_extension (source
, extension_name
);
561 if (e_source_mail_submission_get_use_sent_folder (ms
))
562 sent_folder
= e_source_mail_submission_get_sent_folder (ms
);
563 transport_uid
= e_source_mail_submission_get_transport_uid (ms
);
565 medium
= CAMEL_MEDIUM (message
);
567 /* From: / Resent-From: */
568 addr
= camel_internet_address_new ();
569 camel_internet_address_add (addr
, name
, address
);
573 value
= camel_address_encode (CAMEL_ADDRESS (addr
));
574 camel_medium_set_header (medium
, "Resent-From", value
);
577 camel_mime_message_set_from (message
, addr
);
579 g_object_unref (addr
);
581 /* X-Evolution-Identity */
582 header_name
= "X-Evolution-Identity";
583 camel_medium_set_header (medium
, header_name
, uid
);
585 /* X-Evolution-Fcc */
586 header_name
= "X-Evolution-Fcc";
587 camel_medium_set_header (medium
, header_name
, sent_folder
);
589 /* X-Evolution-Transport */
590 header_name
= "X-Evolution-Transport";
591 camel_medium_set_header (medium
, header_name
, transport_uid
);
593 g_object_unref (source
);
597 reply_to
= e_composer_header_table_get_reply_to (table
);
598 if (reply_to
!= NULL
&& *reply_to
!= '\0') {
599 CamelInternetAddress
*addr
;
601 addr
= camel_internet_address_new ();
603 if (camel_address_unformat (CAMEL_ADDRESS (addr
), reply_to
) > 0)
604 camel_mime_message_set_reply_to (message
, addr
);
606 g_object_unref (addr
);
610 header
= e_composer_header_table_get_header (
611 table
, E_COMPOSER_HEADER_TO
);
612 if (e_composer_header_get_visible (header
)) {
613 EDestination
**to
, **cc
, **bcc
;
615 to
= e_composer_header_table_get_destinations_to (table
);
616 cc
= e_composer_header_table_get_destinations_cc (table
);
617 bcc
= e_composer_header_table_get_destinations_bcc (table
);
619 set_recipients_from_destv (message
, to
, cc
, bcc
, redirect
);
621 e_destination_freev (to
);
622 e_destination_freev (cc
);
623 e_destination_freev (bcc
);
627 camel_mime_message_set_date (message
, CAMEL_MESSAGE_DATE_CURRENT
, 0);
629 /* X-Evolution-PostTo: */
630 header
= e_composer_header_table_get_header (
631 table
, E_COMPOSER_HEADER_POST_TO
);
632 if (e_composer_header_get_visible (header
)) {
634 const gchar
*name
= "X-Evolution-PostTo";
637 medium
= CAMEL_MEDIUM (message
);
638 camel_medium_remove_header (medium
, name
);
640 list
= e_composer_header_table_get_post_to (table
);
641 for (iter
= list
; iter
!= NULL
; iter
= iter
->next
) {
642 gchar
*folder
= iter
->data
;
643 camel_medium_add_header (medium
, name
, folder
);
651 g_free (alias_address
);
654 static CamelCipherHash
655 account_hash_algo_to_camel_hash (const gchar
*hash_algo
)
657 CamelCipherHash res
= CAMEL_CIPHER_HASH_DEFAULT
;
659 if (hash_algo
&& *hash_algo
) {
660 if (g_ascii_strcasecmp (hash_algo
, "sha1") == 0)
661 res
= CAMEL_CIPHER_HASH_SHA1
;
662 else if (g_ascii_strcasecmp (hash_algo
, "sha256") == 0)
663 res
= CAMEL_CIPHER_HASH_SHA256
;
664 else if (g_ascii_strcasecmp (hash_algo
, "sha384") == 0)
665 res
= CAMEL_CIPHER_HASH_SHA384
;
666 else if (g_ascii_strcasecmp (hash_algo
, "sha512") == 0)
667 res
= CAMEL_CIPHER_HASH_SHA512
;
674 composer_add_charset_filter (CamelStream
*stream
,
675 const gchar
*charset
)
677 CamelMimeFilter
*filter
;
679 filter
= camel_mime_filter_charset_new ("UTF-8", charset
);
680 camel_stream_filter_add (CAMEL_STREAM_FILTER (stream
), filter
);
681 g_object_unref (filter
);
685 composer_add_quoted_printable_filter (CamelStream
*stream
)
687 CamelMimeFilter
*filter
;
689 filter
= camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_QP_ENC
);
690 camel_stream_filter_add (CAMEL_STREAM_FILTER (stream
), filter
);
691 g_object_unref (filter
);
694 /* Helper for composer_build_message_thread() */
696 composer_build_message_pgp (AsyncContext
*context
,
697 GCancellable
*cancellable
,
700 ESourceOpenPGP
*extension
;
701 CamelCipherContext
*cipher
;
702 CamelDataWrapper
*content
;
703 CamelMimePart
*mime_part
;
704 const gchar
*extension_name
;
705 const gchar
*pgp_key_id
;
706 const gchar
*signing_algorithm
;
707 gboolean always_trust
;
708 gboolean encrypt_to_self
;
709 gboolean prefer_inline
;
711 /* Return silently if we're not signing or encrypting with PGP. */
712 if (!context
->pgp_sign
&& !context
->pgp_encrypt
)
715 extension_name
= E_SOURCE_EXTENSION_OPENPGP
;
716 extension
= e_source_get_extension (context
->source
, extension_name
);
718 always_trust
= e_source_openpgp_get_always_trust (extension
);
719 encrypt_to_self
= e_source_openpgp_get_encrypt_to_self (extension
);
720 prefer_inline
= e_source_openpgp_get_prefer_inline (extension
);
721 pgp_key_id
= e_source_openpgp_get_key_id (extension
);
722 signing_algorithm
= e_source_openpgp_get_signing_algorithm (extension
);
724 mime_part
= camel_mime_part_new ();
726 camel_medium_set_content (
727 CAMEL_MEDIUM (mime_part
),
728 context
->top_level_part
);
730 if (context
->top_level_part
== context
->text_plain_part
)
731 camel_mime_part_set_encoding (
732 mime_part
, context
->plain_encoding
);
734 g_object_unref (context
->top_level_part
);
735 context
->top_level_part
= NULL
;
737 if (pgp_key_id
== NULL
|| *pgp_key_id
== '\0')
738 camel_internet_address_get (
739 context
->from
, 0, NULL
, &pgp_key_id
);
741 if (context
->pgp_sign
) {
742 CamelMimePart
*npart
;
745 npart
= camel_mime_part_new ();
747 cipher
= camel_gpg_context_new (context
->session
);
748 camel_gpg_context_set_always_trust (CAMEL_GPG_CONTEXT (cipher
), always_trust
);
749 camel_gpg_context_set_prefer_inline (CAMEL_GPG_CONTEXT (cipher
), prefer_inline
);
751 success
= camel_cipher_context_sign_sync (
753 account_hash_algo_to_camel_hash (signing_algorithm
),
754 mime_part
, npart
, cancellable
, error
);
756 g_object_unref (cipher
);
758 g_object_unref (mime_part
);
761 g_object_unref (npart
);
768 if (context
->pgp_encrypt
) {
769 CamelMimePart
*npart
;
772 npart
= camel_mime_part_new ();
774 /* Check to see if we should encrypt to self.
775 * NB: Gets removed immediately after use. */
776 if (encrypt_to_self
&& pgp_key_id
!= NULL
)
779 g_strdup (pgp_key_id
));
781 cipher
= camel_gpg_context_new (context
->session
);
782 camel_gpg_context_set_always_trust (CAMEL_GPG_CONTEXT (cipher
), always_trust
);
783 camel_gpg_context_set_prefer_inline (CAMEL_GPG_CONTEXT (cipher
), prefer_inline
);
785 success
= camel_cipher_context_encrypt_sync (
786 cipher
, pgp_key_id
, context
->recipients
,
787 mime_part
, npart
, cancellable
, error
);
789 g_object_unref (cipher
);
791 if (encrypt_to_self
&& pgp_key_id
!= NULL
)
792 g_ptr_array_set_size (
794 context
->recipients
->len
- 1);
796 g_object_unref (mime_part
);
799 g_object_unref (npart
);
806 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
807 context
->top_level_part
= g_object_ref (content
);
809 g_object_unref (mime_part
);
816 composer_build_message_smime (AsyncContext
*context
,
817 GCancellable
*cancellable
,
820 ESourceSMIME
*extension
;
821 CamelCipherContext
*cipher
;
822 CamelMimePart
*mime_part
;
823 const gchar
*extension_name
;
824 const gchar
*signing_algorithm
;
825 const gchar
*signing_certificate
;
826 const gchar
*encryption_certificate
;
827 gboolean encrypt_to_self
;
828 gboolean have_signing_certificate
;
829 gboolean have_encryption_certificate
;
831 /* Return silently if we're not signing or encrypting with S/MIME. */
832 if (!context
->smime_sign
&& !context
->smime_encrypt
)
835 extension_name
= E_SOURCE_EXTENSION_SMIME
;
836 extension
= e_source_get_extension (context
->source
, extension_name
);
839 e_source_smime_get_encrypt_to_self (extension
);
842 e_source_smime_get_signing_algorithm (extension
);
844 signing_certificate
=
845 e_source_smime_get_signing_certificate (extension
);
847 encryption_certificate
=
848 e_source_smime_get_encryption_certificate (extension
);
850 have_signing_certificate
=
851 (signing_certificate
!= NULL
) &&
852 (*signing_certificate
!= '\0');
854 have_encryption_certificate
=
855 (encryption_certificate
!= NULL
) &&
856 (*encryption_certificate
!= '\0');
858 if (context
->smime_sign
&& !have_signing_certificate
) {
860 error
, CAMEL_ERROR
, CAMEL_ERROR_GENERIC
,
861 _("Cannot sign outgoing message: "
862 "No signing certificate set for "
867 if (context
->smime_encrypt
&& !have_encryption_certificate
) {
869 error
, CAMEL_ERROR
, CAMEL_ERROR_GENERIC
,
870 _("Cannot encrypt outgoing message: "
871 "No encryption certificate set for "
876 mime_part
= camel_mime_part_new ();
878 camel_medium_set_content (
879 CAMEL_MEDIUM (mime_part
),
880 context
->top_level_part
);
882 if (context
->top_level_part
== context
->text_plain_part
)
883 camel_mime_part_set_encoding (
884 mime_part
, context
->plain_encoding
);
886 g_object_unref (context
->top_level_part
);
887 context
->top_level_part
= NULL
;
889 if (context
->smime_sign
) {
890 CamelMimePart
*npart
;
893 npart
= camel_mime_part_new ();
895 cipher
= camel_smime_context_new (context
->session
);
897 /* if we're also encrypting, envelope-sign rather than clear-sign */
898 if (context
->smime_encrypt
) {
899 camel_smime_context_set_sign_mode (
900 (CamelSMIMEContext
*) cipher
,
901 CAMEL_SMIME_SIGN_ENVELOPED
);
902 camel_smime_context_set_encrypt_key (
903 (CamelSMIMEContext
*) cipher
,
904 TRUE
, encryption_certificate
);
905 } else if (have_encryption_certificate
) {
906 camel_smime_context_set_encrypt_key (
907 (CamelSMIMEContext
*) cipher
,
908 TRUE
, encryption_certificate
);
911 success
= camel_cipher_context_sign_sync (
912 cipher
, signing_certificate
,
913 account_hash_algo_to_camel_hash (signing_algorithm
),
914 mime_part
, npart
, cancellable
, error
);
916 g_object_unref (cipher
);
918 g_object_unref (mime_part
);
921 g_object_unref (npart
);
928 if (context
->smime_encrypt
) {
931 /* Check to see if we should encrypt to self.
932 * NB: Gets removed immediately after use. */
935 context
->recipients
, g_strdup (
936 encryption_certificate
));
938 cipher
= camel_smime_context_new (context
->session
);
939 camel_smime_context_set_encrypt_key (
940 (CamelSMIMEContext
*) cipher
, TRUE
,
941 encryption_certificate
);
943 success
= camel_cipher_context_encrypt_sync (
945 context
->recipients
, mime_part
,
946 CAMEL_MIME_PART (context
->message
),
949 g_object_unref (cipher
);
955 g_ptr_array_set_size (
957 context
->recipients
->len
- 1);
960 /* we replaced the message directly, we don't want to do reparenting foo */
961 if (context
->smime_encrypt
) {
962 context
->skip_content
= TRUE
;
964 CamelDataWrapper
*content
;
966 content
= camel_medium_get_content (
967 CAMEL_MEDIUM (mime_part
));
968 context
->top_level_part
= g_object_ref (content
);
971 g_object_unref (mime_part
);
978 composer_build_message_thread (GSimpleAsyncResult
*simple
,
979 EMsgComposer
*composer
,
980 GCancellable
*cancellable
)
982 AsyncContext
*context
;
983 GError
*error
= NULL
;
985 context
= g_simple_async_result_get_op_res_gpointer (simple
);
987 /* Setup working recipient list if we're encrypting. */
988 if (context
->pgp_encrypt
|| context
->smime_encrypt
) {
991 const gchar
*types
[] = {
992 CAMEL_RECIPIENT_TYPE_TO
,
993 CAMEL_RECIPIENT_TYPE_CC
,
994 CAMEL_RECIPIENT_TYPE_BCC
997 context
->recipients
= g_ptr_array_new_with_free_func (
998 (GDestroyNotify
) g_free
);
999 for (ii
= 0; ii
< G_N_ELEMENTS (types
); ii
++) {
1000 CamelInternetAddress
*addr
;
1001 const gchar
*address
;
1003 addr
= camel_mime_message_get_recipients (
1004 context
->message
, types
[ii
]);
1005 for (jj
= 0; camel_internet_address_get (addr
, jj
, NULL
, &address
); jj
++)
1007 context
->recipients
,
1008 g_strdup (address
));
1012 if (!composer_build_message_pgp (context
, cancellable
, &error
)) {
1013 g_simple_async_result_take_error (simple
, error
);
1017 #if defined (ENABLE_SMIME)
1018 if (!composer_build_message_smime (context
, cancellable
, &error
)) {
1019 g_simple_async_result_take_error (simple
, error
);
1022 #endif /* ENABLE_SMIME */
1026 composer_add_evolution_composer_mode_header (CamelMedium
*medium
,
1027 EMsgComposer
*composer
)
1030 EHTMLEditor
*editor
;
1031 EContentEditor
*cnt_editor
;
1033 editor
= e_msg_composer_get_editor (composer
);
1034 cnt_editor
= e_html_editor_get_content_editor (editor
);
1035 html_mode
= e_content_editor_get_html_mode (cnt_editor
);
1037 camel_medium_add_header (
1039 "X-Evolution-Composer-Mode",
1040 html_mode
? "text/html" : "text/plain");
1044 composer_add_evolution_format_header (CamelMedium
*medium
,
1045 ComposerFlags flags
)
1049 string
= g_string_sized_new (128);
1051 if ((flags
& COMPOSER_FLAG_HTML_CONTENT
) || (flags
& COMPOSER_FLAG_SAVE_DRAFT
))
1052 g_string_append (string
, "text/html");
1054 g_string_append (string
, "text/plain");
1056 if (flags
& COMPOSER_FLAG_PGP_SIGN
)
1057 g_string_append (string
, ", pgp-sign");
1059 if (flags
& COMPOSER_FLAG_PGP_ENCRYPT
)
1060 g_string_append (string
, ", pgp-encrypt");
1062 if (flags
& COMPOSER_FLAG_SMIME_SIGN
)
1063 g_string_append (string
, ", smime-sign");
1065 if (flags
& COMPOSER_FLAG_SMIME_ENCRYPT
)
1066 g_string_append (string
, ", smime-encrypt");
1068 camel_medium_add_header (
1069 medium
, "X-Evolution-Format", string
->str
);
1071 g_string_free (string
, TRUE
);
1075 composer_build_message (EMsgComposer
*composer
,
1076 ComposerFlags flags
,
1078 GCancellable
*cancellable
,
1079 GAsyncReadyCallback callback
,
1082 EMsgComposerPrivate
*priv
;
1083 GSimpleAsyncResult
*simple
;
1084 AsyncContext
*context
;
1085 EAttachmentView
*view
;
1086 EAttachmentStore
*store
;
1087 EComposerHeaderTable
*table
;
1088 CamelDataWrapper
*html
;
1089 ESourceMailIdentity
*mi
;
1090 const gchar
*extension_name
;
1091 const gchar
*iconv_charset
= NULL
;
1092 const gchar
*organization
;
1093 gchar
*identity_uid
;
1094 CamelMultipart
*body
= NULL
;
1095 CamelContentType
*type
;
1096 CamelStream
*stream
;
1097 CamelStream
*mem_stream
;
1098 CamelMimePart
*part
;
1101 gchar
*charset
, *message_uid
;
1102 const gchar
*from_domain
;
1105 priv
= composer
->priv
;
1106 table
= e_msg_composer_get_header_table (composer
);
1107 view
= e_msg_composer_get_attachment_view (composer
);
1108 store
= e_attachment_view_get_store (view
);
1110 identity_uid
= e_composer_header_table_dup_identity_uid (table
, NULL
, NULL
);
1112 source
= e_composer_header_table_ref_source (table
, identity_uid
);
1113 g_free (identity_uid
);
1115 g_warn_if_fail (source
!= NULL
);
1120 /* Do all the non-blocking work here, and defer
1121 * any blocking operations to a separate thread. */
1123 context
= g_slice_new0 (AsyncContext
);
1124 context
->source
= source
; /* takes the reference */
1125 context
->session
= e_msg_composer_ref_session (composer
);
1126 context
->from
= e_msg_composer_get_from (composer
);
1128 if (!(flags
& COMPOSER_FLAG_SAVE_DRAFT
)) {
1129 if (flags
& COMPOSER_FLAG_PGP_SIGN
)
1130 context
->pgp_sign
= TRUE
;
1132 if (flags
& COMPOSER_FLAG_PGP_ENCRYPT
)
1133 context
->pgp_encrypt
= TRUE
;
1135 if (flags
& COMPOSER_FLAG_SMIME_SIGN
)
1136 context
->smime_sign
= TRUE
;
1138 if (flags
& COMPOSER_FLAG_SMIME_ENCRYPT
)
1139 context
->smime_encrypt
= TRUE
;
1142 context
->need_thread
=
1143 context
->pgp_sign
|| context
->pgp_encrypt
||
1144 context
->smime_sign
|| context
->smime_encrypt
;
1146 simple
= g_simple_async_result_new (
1147 G_OBJECT (composer
), callback
,
1148 user_data
, composer_build_message
);
1150 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
1152 g_simple_async_result_set_op_res_gpointer (
1153 simple
, context
, (GDestroyNotify
) async_context_free
);
1155 /* If this is a redirected message, just tweak the headers. */
1156 if (priv
->redirect
) {
1157 context
->skip_content
= TRUE
;
1158 context
->message
= g_object_ref (priv
->redirect
);
1159 build_message_headers (composer
, context
->message
, TRUE
);
1160 g_simple_async_result_complete (simple
);
1161 g_object_unref (simple
);
1165 context
->message
= camel_mime_message_new ();
1167 if (context
->from
&& camel_internet_address_get (context
->from
, 0, NULL
, &from_domain
)) {
1168 const gchar
*at
= strchr (from_domain
, '@');
1170 from_domain
= at
+ 1;
1177 if (!from_domain
|| !*from_domain
)
1178 from_domain
= "localhost";
1180 message_uid
= camel_header_msgid_generate (from_domain
);
1182 /* Explicitly generate a Message-ID header here so it's
1183 * consistent for all outbound streams (SMTP, Fcc, etc). */
1184 camel_mime_message_set_message_id (context
->message
, message_uid
);
1185 g_free (message_uid
);
1187 build_message_headers (composer
, context
->message
, FALSE
);
1188 for (i
= 0; i
< priv
->extra_hdr_names
->len
; i
++) {
1189 camel_medium_add_header (
1190 CAMEL_MEDIUM (context
->message
),
1191 priv
->extra_hdr_names
->pdata
[i
],
1192 priv
->extra_hdr_values
->pdata
[i
]);
1196 extension_name
= E_SOURCE_EXTENSION_MAIL_IDENTITY
;
1197 mi
= e_source_get_extension (source
, extension_name
);
1198 organization
= e_source_mail_identity_get_organization (mi
);
1200 /* Disposition-Notification-To */
1201 if (flags
& COMPOSER_FLAG_REQUEST_READ_RECEIPT
) {
1202 EComposerHeader
*header
;
1203 const gchar
*mdn_address
;
1205 header
= e_composer_header_table_get_header (table
, E_COMPOSER_HEADER_REPLY_TO
);
1206 mdn_address
= e_composer_text_header_get_text (E_COMPOSER_TEXT_HEADER (header
));
1208 if (!mdn_address
|| !*mdn_address
) {
1209 header
= e_composer_header_table_get_header (table
, E_COMPOSER_HEADER_FROM
);
1210 mdn_address
= e_composer_from_header_get_address (E_COMPOSER_FROM_HEADER (header
));
1213 if (!mdn_address
|| !*mdn_address
)
1214 mdn_address
= e_source_mail_identity_get_reply_to (mi
);
1215 if (mdn_address
== NULL
)
1216 mdn_address
= e_source_mail_identity_get_address (mi
);
1217 if (mdn_address
!= NULL
)
1218 camel_medium_add_header (
1219 CAMEL_MEDIUM (context
->message
),
1220 "Disposition-Notification-To", mdn_address
);
1224 if (organization
!= NULL
&& *organization
!= '\0') {
1225 gchar
*encoded_organization
;
1227 encoded_organization
= camel_header_encode_string (
1228 (const guchar
*) organization
);
1229 camel_medium_set_header (
1230 CAMEL_MEDIUM (context
->message
),
1231 "Organization", encoded_organization
);
1232 g_free (encoded_organization
);
1237 if (flags
& COMPOSER_FLAG_PRIORITIZE_MESSAGE
)
1238 camel_medium_add_header (
1239 CAMEL_MEDIUM (context
->message
),
1242 /* Build the text/plain part. */
1244 if (priv
->mime_body
) {
1245 if (text_requires_quoted_printable (priv
->mime_body
, -1)) {
1246 context
->plain_encoding
=
1247 CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
;
1249 context
->plain_encoding
= CAMEL_TRANSFER_ENCODING_7BIT
;
1250 for (i
= 0; priv
->mime_body
[i
]; i
++) {
1251 if ((guchar
) priv
->mime_body
[i
] > 127) {
1252 context
->plain_encoding
=
1253 CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
;
1259 data
= g_byte_array_new ();
1260 g_byte_array_append (
1261 data
, (const guint8
*) priv
->mime_body
,
1262 strlen (priv
->mime_body
));
1263 type
= camel_content_type_decode (priv
->mime_type
);
1267 EHTMLEditor
*editor
;
1268 EContentEditor
*cnt_editor
;
1270 editor
= e_msg_composer_get_editor (composer
);
1271 cnt_editor
= e_html_editor_get_content_editor (editor
);
1272 data
= g_byte_array_new ();
1274 text
= e_content_editor_get_content (
1276 E_CONTENT_EDITOR_GET_TEXT_PLAIN
|
1277 E_CONTENT_EDITOR_GET_PROCESSED
,
1281 g_warning ("%s: Failed to retrieve text/plain processed content", G_STRFUNC
);
1282 text
= g_strdup ("");
1285 g_byte_array_append (data
, (guint8
*) text
, strlen (text
));
1288 type
= camel_content_type_new ("text", "plain");
1289 charset
= best_charset (
1290 data
, priv
->charset
, &context
->plain_encoding
);
1291 if (charset
!= NULL
) {
1292 camel_content_type_set_param (type
, "charset", charset
);
1293 iconv_charset
= camel_iconv_charset_name (charset
);
1298 mem_stream
= camel_stream_mem_new_with_byte_array (data
);
1299 stream
= camel_stream_filter_new (mem_stream
);
1300 g_object_unref (mem_stream
);
1302 /* Convert the stream to the appropriate charset. */
1303 if (iconv_charset
&& g_ascii_strcasecmp (iconv_charset
, "UTF-8") != 0)
1304 composer_add_charset_filter (stream
, iconv_charset
);
1306 /* Encode the stream to quoted-printable if necessary. */
1307 if (context
->plain_encoding
== CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
)
1308 composer_add_quoted_printable_filter (stream
);
1310 /* Construct the content object. This does not block since
1311 * we're constructing the data wrapper from a memory stream. */
1312 context
->top_level_part
= camel_data_wrapper_new ();
1313 camel_data_wrapper_construct_from_stream_sync (
1314 context
->top_level_part
, stream
, NULL
, NULL
);
1315 g_object_unref (stream
);
1317 context
->text_plain_part
= g_object_ref (context
->top_level_part
);
1319 /* Avoid re-encoding the data when adding it to a MIME part. */
1320 if (context
->plain_encoding
== CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
)
1321 camel_data_wrapper_set_encoding (context
->top_level_part
, context
->plain_encoding
);
1323 camel_data_wrapper_set_mime_type_field (
1324 context
->top_level_part
, type
);
1326 camel_content_type_unref (type
);
1328 /* Build the text/html part, and wrap it and the text/plain part
1329 * in a multipart/alternative part. Additionally, if there are
1330 * inline images then wrap the multipart/alternative part along
1331 * with the images in a multipart/related part.
1333 * So the structure of all this will be:
1336 * multipart/alternative
1339 * image/<<whatever>>
1340 * image/<<whatever>>
1344 if ((flags
& COMPOSER_FLAG_HTML_CONTENT
) != 0 ||
1345 (flags
& COMPOSER_FLAG_SAVE_DRAFT
) != 0) {
1348 gboolean pre_encode
;
1349 EHTMLEditor
*editor
;
1350 EContentEditor
*cnt_editor
;
1351 GSList
*inline_images_parts
= NULL
, *link
;
1353 editor
= e_msg_composer_get_editor (composer
);
1354 cnt_editor
= e_html_editor_get_content_editor (editor
);
1356 data
= g_byte_array_new ();
1357 if ((flags
& COMPOSER_FLAG_SAVE_DRAFT
) != 0) {
1358 /* X-Evolution-Format */
1359 composer_add_evolution_format_header (
1360 CAMEL_MEDIUM (context
->message
), flags
);
1362 /* X-Evolution-Composer-Mode */
1363 composer_add_evolution_composer_mode_header (
1364 CAMEL_MEDIUM (context
->message
), composer
);
1366 text
= e_content_editor_get_content (
1368 E_CONTENT_EDITOR_GET_TEXT_HTML
|
1369 E_CONTENT_EDITOR_GET_INLINE_IMAGES
,
1370 from_domain
, &inline_images_parts
);
1373 g_warning ("%s: Failed to retrieve draft content", G_STRFUNC
);
1374 text
= g_strdup ("");
1377 text
= e_content_editor_get_content (
1379 E_CONTENT_EDITOR_GET_TEXT_HTML
|
1380 E_CONTENT_EDITOR_GET_PROCESSED
|
1381 E_CONTENT_EDITOR_GET_INLINE_IMAGES
,
1382 from_domain
, &inline_images_parts
);
1385 g_warning ("%s: Failed to retrieve HTML processed content", G_STRFUNC
);
1386 text
= g_strdup ("");
1390 length
= strlen (text
);
1391 g_byte_array_append (data
, (guint8
*) text
, (guint
) length
);
1392 pre_encode
= text_requires_quoted_printable (text
, length
);
1395 mem_stream
= camel_stream_mem_new_with_byte_array (data
);
1396 stream
= camel_stream_filter_new (mem_stream
);
1397 g_object_unref (mem_stream
);
1400 composer_add_quoted_printable_filter (stream
);
1402 /* Construct the content object. This does not block since
1403 * we're constructing the data wrapper from a memory stream. */
1404 html
= camel_data_wrapper_new ();
1405 camel_data_wrapper_construct_from_stream_sync (
1406 html
, stream
, NULL
, NULL
);
1407 g_object_unref (stream
);
1409 camel_data_wrapper_set_mime_type (
1410 html
, "text/html; charset=utf-8");
1412 /* Avoid re-encoding the data when adding it to a MIME part. */
1414 camel_data_wrapper_set_encoding (html
, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
);
1416 /* Build the multipart/alternative */
1417 body
= camel_multipart_new ();
1418 camel_data_wrapper_set_mime_type (
1419 CAMEL_DATA_WRAPPER (body
), "multipart/alternative");
1420 camel_multipart_set_boundary (body
, NULL
);
1422 /* Add the text/plain part. */
1423 part
= camel_mime_part_new ();
1424 camel_medium_set_content (
1425 CAMEL_MEDIUM (part
), context
->top_level_part
);
1426 camel_mime_part_set_encoding (part
, context
->plain_encoding
);
1427 camel_multipart_add_part (body
, part
);
1428 g_object_unref (part
);
1430 /* Add the text/html part. */
1431 part
= camel_mime_part_new ();
1432 camel_medium_set_content (CAMEL_MEDIUM (part
), html
);
1433 camel_mime_part_set_encoding (
1434 part
, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE
);
1435 camel_multipart_add_part (body
, part
);
1436 g_object_unref (part
);
1438 g_object_unref (context
->top_level_part
);
1439 g_object_unref (html
);
1441 /* If there are inlined images, construct a multipart/related
1442 * containing the multipart/alternative and the images. */
1443 if (inline_images_parts
) {
1444 CamelMultipart
*html_with_images
;
1446 html_with_images
= camel_multipart_new ();
1447 camel_data_wrapper_set_mime_type (
1448 CAMEL_DATA_WRAPPER (html_with_images
),
1449 "multipart/related; "
1450 "type=\"multipart/alternative\"");
1451 camel_multipart_set_boundary (html_with_images
, NULL
);
1453 part
= camel_mime_part_new ();
1454 camel_medium_set_content (
1455 CAMEL_MEDIUM (part
),
1456 CAMEL_DATA_WRAPPER (body
));
1457 camel_multipart_add_part (html_with_images
, part
);
1458 g_object_unref (part
);
1460 g_object_unref (body
);
1462 for (link
= inline_images_parts
; link
; link
= g_slist_next (link
)) {
1463 CamelMimePart
*part
= link
->data
;
1465 camel_multipart_add_part (html_with_images
, part
);
1468 context
->top_level_part
=
1469 CAMEL_DATA_WRAPPER (html_with_images
);
1471 context
->top_level_part
=
1472 CAMEL_DATA_WRAPPER (body
);
1474 g_slist_free_full (inline_images_parts
, g_object_unref
);
1477 /* If there are attachments, wrap what we've built so far
1478 * along with the attachments in a multipart/mixed part. */
1479 if (e_attachment_store_get_num_attachments (store
) > 0) {
1480 CamelMultipart
*multipart
= camel_multipart_new ();
1482 /* Generate a random boundary. */
1483 camel_multipart_set_boundary (multipart
, NULL
);
1485 part
= camel_mime_part_new ();
1486 camel_medium_set_content (
1487 CAMEL_MEDIUM (part
),
1488 context
->top_level_part
);
1489 if (context
->top_level_part
== context
->text_plain_part
)
1490 camel_mime_part_set_encoding (
1491 part
, context
->plain_encoding
);
1492 camel_multipart_add_part (multipart
, part
);
1493 g_object_unref (part
);
1495 e_attachment_store_add_to_multipart (
1496 store
, multipart
, priv
->charset
);
1498 g_object_unref (context
->top_level_part
);
1499 context
->top_level_part
= CAMEL_DATA_WRAPPER (multipart
);
1502 /* Run any blocking operations in a separate thread. */
1503 if (context
->need_thread
)
1504 g_simple_async_result_run_in_thread (
1505 simple
, (GSimpleAsyncThreadFunc
)
1506 composer_build_message_thread
,
1507 io_priority
, cancellable
);
1509 g_simple_async_result_complete (simple
);
1511 g_object_unref (simple
);
1514 static CamelMimeMessage
*
1515 composer_build_message_finish (EMsgComposer
*composer
,
1516 GAsyncResult
*result
,
1519 GSimpleAsyncResult
*simple
;
1520 AsyncContext
*context
;
1522 g_return_val_if_fail (
1523 g_simple_async_result_is_valid (
1524 result
, G_OBJECT (composer
), composer_build_message
), NULL
);
1526 simple
= G_SIMPLE_ASYNC_RESULT (result
);
1527 context
= g_simple_async_result_get_op_res_gpointer (simple
);
1529 if (g_simple_async_result_propagate_error (simple
, error
))
1532 /* Finalize some details before returning. */
1534 if (!context
->skip_content
) {
1535 if (context
->top_level_part
!= context
->text_plain_part
&&
1536 CAMEL_IS_MIME_PART (context
->top_level_part
)) {
1537 CamelDataWrapper
*content
;
1538 CamelMedium
*imedium
, *omedium
;
1539 const CamelNameValueArray
*headers
;
1541 imedium
= CAMEL_MEDIUM (context
->top_level_part
);
1542 omedium
= CAMEL_MEDIUM (context
->message
);
1544 content
= camel_medium_get_content (imedium
);
1545 camel_medium_set_content (omedium
, content
);
1546 camel_data_wrapper_set_encoding (CAMEL_DATA_WRAPPER (omedium
), camel_data_wrapper_get_encoding (CAMEL_DATA_WRAPPER (imedium
)));
1548 headers
= camel_medium_get_headers (imedium
);
1551 length
= camel_name_value_array_get_length (headers
);
1553 for (ii
= 0; ii
< length
; ii
++) {
1554 const gchar
*header_name
= NULL
;
1555 const gchar
*header_value
= NULL
;
1557 if (camel_name_value_array_get (headers
, ii
, &header_name
, &header_value
))
1558 camel_medium_set_header (omedium
, header_name
, header_value
);
1562 camel_medium_set_content (
1563 CAMEL_MEDIUM (context
->message
),
1564 context
->top_level_part
);
1568 if (context
->top_level_part
== context
->text_plain_part
) {
1569 camel_mime_part_set_encoding (
1570 CAMEL_MIME_PART (context
->message
),
1571 context
->plain_encoding
);
1574 return g_object_ref (context
->message
);
1580 set_editor_text (EMsgComposer
*composer
,
1583 gboolean set_signature
)
1585 EHTMLEditor
*editor
;
1586 EContentEditor
*cnt_editor
;
1588 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
1589 g_return_if_fail (text
!= NULL
);
1591 editor
= e_msg_composer_get_editor (composer
);
1592 cnt_editor
= e_html_editor_get_content_editor (editor
);
1595 e_content_editor_insert_content (
1598 E_CONTENT_EDITOR_INSERT_TEXT_HTML
|
1599 E_CONTENT_EDITOR_INSERT_REPLACE_ALL
);
1601 e_content_editor_insert_content (
1604 E_CONTENT_EDITOR_INSERT_TEXT_PLAIN
|
1605 E_CONTENT_EDITOR_INSERT_REPLACE_ALL
);
1608 e_composer_update_signature (composer
);
1611 /* Miscellaneous callbacks. */
1614 attachment_store_changed_cb (EMsgComposer
*composer
)
1616 EHTMLEditor
*editor
;
1618 /* Mark the editor as changed so it prompts about unsaved
1619 * changes on close. */
1620 editor
= e_msg_composer_get_editor (composer
);
1622 EContentEditor
*cnt_editor
;
1624 cnt_editor
= e_html_editor_get_content_editor (editor
);
1625 e_content_editor_set_changed (cnt_editor
, TRUE
);
1630 msg_composer_subject_changed_cb (EMsgComposer
*composer
)
1632 EComposerHeaderTable
*table
;
1633 const gchar
*subject
;
1635 table
= e_msg_composer_get_header_table (composer
);
1636 subject
= e_composer_header_table_get_subject (table
);
1638 if (subject
== NULL
|| *subject
== '\0')
1639 subject
= _("Compose Message");
1641 gtk_window_set_title (GTK_WINDOW (composer
), subject
);
1645 msg_composer_mail_identity_changed_cb (EMsgComposer
*composer
)
1647 EMailSignatureComboBox
*combo_box
;
1648 ESourceMailComposition
*mc
;
1649 ESourceOpenPGP
*pgp
;
1650 ESourceSMIME
*smime
;
1651 EComposerHeaderTable
*table
;
1652 EContentEditor
*cnt_editor
;
1653 GtkToggleAction
*action
;
1658 gboolean pgp_encrypt
;
1659 gboolean smime_sign
;
1660 gboolean smime_encrypt
;
1661 gboolean composer_realized
;
1662 gboolean was_disable_signature
, unset_signature
= FALSE
;
1663 const gchar
*extension_name
;
1664 const gchar
*active_signature_id
;
1665 gchar
*uid
, *alias_name
= NULL
, *alias_address
= NULL
, *pgp_keyid
, *smime_cert
;
1667 cnt_editor
= e_html_editor_get_content_editor (e_msg_composer_get_editor (composer
));
1668 table
= e_msg_composer_get_header_table (composer
);
1669 uid
= e_composer_header_table_dup_identity_uid (table
, &alias_name
, &alias_address
);
1671 /* Silently return if no identity is selected. */
1673 e_content_editor_set_start_bottom (cnt_editor
, E_THREE_STATE_INCONSISTENT
);
1674 e_content_editor_set_top_signature (cnt_editor
, E_THREE_STATE_INCONSISTENT
);
1676 g_free (alias_name
);
1677 g_free (alias_address
);
1681 source
= e_composer_header_table_ref_source (table
, uid
);
1682 g_return_if_fail (source
!= NULL
);
1684 extension_name
= E_SOURCE_EXTENSION_MAIL_COMPOSITION
;
1685 mc
= e_source_get_extension (source
, extension_name
);
1687 e_content_editor_set_start_bottom (cnt_editor
,
1688 e_source_mail_composition_get_start_bottom (mc
));
1689 e_content_editor_set_top_signature (cnt_editor
,
1690 e_source_mail_composition_get_top_signature (mc
));
1692 extension_name
= E_SOURCE_EXTENSION_OPENPGP
;
1693 pgp
= e_source_get_extension (source
, extension_name
);
1694 pgp_keyid
= e_source_openpgp_dup_key_id (pgp
);
1695 pgp_sign
= pgp_keyid
&& *pgp_keyid
&& e_source_openpgp_get_sign_by_default (pgp
);
1696 pgp_encrypt
= pgp_keyid
&& *pgp_keyid
&& e_source_openpgp_get_encrypt_by_default (pgp
);
1699 extension_name
= E_SOURCE_EXTENSION_SMIME
;
1700 smime
= e_source_get_extension (source
, extension_name
);
1701 smime_cert
= e_source_smime_dup_signing_certificate (smime
);
1702 smime_sign
= smime_cert
&& *smime_cert
&& e_source_smime_get_sign_by_default (smime
);
1703 g_free (smime_cert
);
1704 smime_cert
= e_source_smime_dup_encryption_certificate (smime
);
1705 smime_encrypt
= smime_cert
&& *smime_cert
&& e_source_smime_get_encrypt_by_default (smime
);
1706 g_free (smime_cert
);
1709 (composer
->priv
->mime_type
== NULL
) ||
1710 e_source_mail_composition_get_sign_imip (mc
) ||
1711 (g_ascii_strncasecmp (
1712 composer
->priv
->mime_type
,
1713 "text/calendar", 13) != 0);
1715 /* Preserve options only if the composer was realized, otherwise an account
1716 change according to current folder or similar reasons can cause the options
1717 to be set, when the default account has it set, but the other not. */
1718 composer_realized
= gtk_widget_get_realized (GTK_WIDGET (composer
));
1720 action
= GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
));
1721 active
= composer_realized
&& gtk_toggle_action_get_active (action
);
1722 active
|= (can_sign
&& pgp_sign
);
1723 gtk_toggle_action_set_active (action
, active
);
1725 action
= GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
));
1726 active
= composer_realized
&& gtk_toggle_action_get_active (action
);
1727 active
|= pgp_encrypt
;
1728 gtk_toggle_action_set_active (action
, active
);
1730 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
));
1731 active
= composer_realized
&& gtk_toggle_action_get_active (action
);
1732 active
|= (can_sign
&& smime_sign
);
1733 gtk_toggle_action_set_active (action
, active
);
1735 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
));
1736 active
= composer_realized
&& gtk_toggle_action_get_active (action
);
1737 active
|= smime_encrypt
;
1738 gtk_toggle_action_set_active (action
, active
);
1740 was_disable_signature
= composer
->priv
->disable_signature
;
1742 if (e_msg_composer_get_is_reply_or_forward (composer
)) {
1743 GSettings
*settings
;
1745 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
1746 unset_signature
= g_settings_get_boolean (settings
, "composer-signature-in-new-only");
1747 g_object_unref (settings
);
1750 combo_box
= e_composer_header_table_get_signature_combo_box (table
);
1752 if (unset_signature
)
1753 composer
->priv
->disable_signature
= TRUE
;
1755 e_mail_signature_combo_box_set_identity (combo_box
, uid
, alias_name
, alias_address
);
1757 if (unset_signature
)
1758 gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box
), "none");
1760 composer
->priv
->disable_signature
= was_disable_signature
;
1762 g_object_unref (source
);
1765 active_signature_id
= gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box
));
1766 if (unset_signature
|| g_strcmp0 (active_signature_id
, E_MAIL_SIGNATURE_AUTOGENERATED_UID
) == 0)
1767 e_composer_update_signature (composer
);
1769 g_free (alias_name
);
1770 g_free (alias_address
);
1774 msg_composer_paste_clipboard_targets_cb (GtkClipboard
*clipboard
,
1777 EMsgComposer
*composer
)
1779 EHTMLEditor
*editor
;
1780 EContentEditor
*cnt_editor
;
1782 if (targets
== NULL
|| n_targets
< 0)
1785 editor
= e_msg_composer_get_editor (composer
);
1786 cnt_editor
= e_html_editor_get_content_editor (editor
);
1788 if (!e_content_editor_get_html_mode (cnt_editor
) &&
1789 gtk_targets_include_image (targets
, n_targets
, TRUE
)) {
1790 e_composer_paste_image (composer
, clipboard
);
1794 if (gtk_targets_include_uri (targets
, n_targets
)) {
1795 e_composer_paste_uris (composer
, clipboard
);
1799 /* Order is important here to ensure common use cases are
1800 * handled correctly. See GNOME bug #603715 for details. */
1801 if (gtk_targets_include_text (targets
, n_targets
) ||
1802 e_targets_include_html (targets
, n_targets
)) {
1803 if (composer
->priv
->last_signal_was_paste_primary
) {
1804 e_content_editor_paste_primary (cnt_editor
);
1806 e_content_editor_paste (cnt_editor
);
1810 if (composer
->priv
->last_signal_was_paste_primary
) {
1811 e_content_editor_paste_primary (cnt_editor
);
1813 e_content_editor_paste (cnt_editor
);
1817 msg_composer_paste_primary_clipboard_cb (EContentEditor
*cnt_editor
,
1818 EMsgComposer
*composer
)
1820 GtkClipboard
*clipboard
;
1821 GdkAtom
*targets
= NULL
;
1824 clipboard
= gtk_clipboard_get (GDK_SELECTION_PRIMARY
);
1826 composer
->priv
->last_signal_was_paste_primary
= TRUE
;
1828 if (gtk_clipboard_wait_for_targets (clipboard
, &targets
, &n_targets
)) {
1829 msg_composer_paste_clipboard_targets_cb (clipboard
, targets
, n_targets
, composer
);
1837 msg_composer_paste_clipboard_cb (EContentEditor
*cnt_editor
,
1838 EMsgComposer
*composer
)
1840 GtkClipboard
*clipboard
;
1841 GdkAtom
*targets
= NULL
;
1844 clipboard
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
);
1846 composer
->priv
->last_signal_was_paste_primary
= FALSE
;
1848 if (gtk_clipboard_wait_for_targets (clipboard
, &targets
, &n_targets
)) {
1849 msg_composer_paste_clipboard_targets_cb (clipboard
, targets
, n_targets
, composer
);
1857 msg_composer_drag_data_received_cb (GtkWidget
*widget
,
1858 GdkDragContext
*context
,
1861 GtkSelectionData
*selection
,
1864 EMsgComposer
*composer
)
1866 EHTMLEditor
*editor
;
1867 EContentEditor
*cnt_editor
;
1868 gboolean html_mode
, is_move
;
1870 editor
= e_msg_composer_get_editor (composer
);
1871 cnt_editor
= e_html_editor_get_content_editor (editor
);
1872 html_mode
= e_content_editor_get_html_mode (cnt_editor
);
1874 g_signal_handler_disconnect (cnt_editor
, composer
->priv
->drag_data_received_handler_id
);
1875 composer
->priv
->drag_data_received_handler_id
= 0;
1877 is_move
= gdk_drag_context_get_selected_action (context
) == GDK_ACTION_MOVE
;
1879 /* HTML mode has a few special cases for drops... */
1880 /* If we're receiving URIs and -all- the URIs point to
1881 * image files, we want the image(s) to be inserted in
1882 * the message body. */
1884 (e_composer_selection_is_image_uris (composer
, selection
) ||
1885 e_composer_selection_is_base64_uris (composer
, selection
))) {
1891 data
= gtk_selection_data_get_data (selection
);
1892 length
= gtk_selection_data_get_length (selection
);
1894 if (!data
|| length
< 0) {
1895 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
1899 e_content_editor_move_caret_on_coordinates (cnt_editor
, x
, y
, FALSE
);
1903 uri
= e_util_next_uri_from_uri_list ((guchar
**) &data
, &len
, &list_len
);
1904 e_content_editor_insert_image (cnt_editor
, uri
);
1908 gtk_drag_finish (context
, TRUE
, is_move
, time
);
1910 EAttachmentView
*attachment_view
=
1911 e_msg_composer_get_attachment_view (composer
);
1912 /* Forward the data to the attachment view. Note that calling
1913 * e_attachment_view_drag_data_received() will not work because
1914 * that function only handles the case where all the other drag
1915 * handlers have failed. */
1916 e_attachment_paned_drag_data_received (
1917 E_ATTACHMENT_PANED (attachment_view
),
1918 context
, x
, y
, selection
, info
, time
);
1923 msg_composer_drag_drop_cb (GtkWidget
*widget
,
1924 GdkDragContext
*context
,
1928 EMsgComposer
*composer
)
1930 GdkAtom target
= gtk_drag_dest_find_target (widget
, context
, NULL
);
1932 if (target
== GDK_NONE
) {
1933 gdk_drag_status (context
, 0, time
);
1935 composer
->priv
->drag_data_received_handler_id
= g_signal_connect (
1936 E_CONTENT_EDITOR (widget
), "drag-data-received",
1937 G_CALLBACK (msg_composer_drag_data_received_cb
), composer
);
1939 gtk_drag_get_data (widget
, context
, target
, time
);
1948 msg_composer_drop_handled_cb (EContentEditor
*cnt_editor
,
1949 EMsgComposer
*composer
)
1951 if (composer
->priv
->drag_data_received_handler_id
!= 0) {
1952 g_signal_handler_disconnect (cnt_editor
, composer
->priv
->drag_data_received_handler_id
);
1953 composer
->priv
->drag_data_received_handler_id
= 0;
1958 msg_composer_drag_begin_cb (GtkWidget
*widget
,
1959 GdkDragContext
*context
,
1960 EMsgComposer
*composer
)
1962 if (composer
->priv
->drag_data_received_handler_id
!= 0) {
1963 g_signal_handler_disconnect (E_CONTENT_EDITOR( widget
), composer
->priv
->drag_data_received_handler_id
);
1964 composer
->priv
->drag_data_received_handler_id
= 0;
1969 msg_composer_notify_header_cb (EMsgComposer
*composer
)
1971 EContentEditor
*cnt_editor
;
1972 EHTMLEditor
*editor
;
1974 editor
= e_msg_composer_get_editor (composer
);
1975 cnt_editor
= e_html_editor_get_content_editor (editor
);
1976 e_content_editor_set_changed (cnt_editor
, TRUE
);
1980 msg_composer_delete_event_cb (EMsgComposer
*composer
)
1982 /* If the "async" action group is insensitive, it means an
1983 * asynchronous operation is in progress. Block the event. */
1984 if (!gtk_action_group_get_sensitive (composer
->priv
->async_actions
))
1987 gtk_action_activate (ACTION (CLOSE
));
1993 msg_composer_realize_cb (EMsgComposer
*composer
)
1995 GSettings
*settings
;
1998 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2000 action
= ACTION (TOOLBAR_PGP_SIGN
);
2001 if (gtk_action_get_visible (action
) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
2002 gtk_action_set_visible (action
, FALSE
);
2004 action
= ACTION (TOOLBAR_PGP_ENCRYPT
);
2005 if (gtk_action_get_visible (action
) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
2006 gtk_action_set_visible (action
, FALSE
);
2008 action
= ACTION (TOOLBAR_SMIME_SIGN
);
2009 if (gtk_action_get_visible (action
) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
2010 gtk_action_set_visible (action
, FALSE
);
2012 action
= ACTION (TOOLBAR_SMIME_ENCRYPT
);
2013 if (gtk_action_get_visible (action
) && !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
2014 gtk_action_set_visible (action
, FALSE
);
2016 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
2018 if (g_settings_get_boolean (settings
, "composer-toolbar-show-sign-encrypt")) {
2019 EComposerHeaderTable
*table
;
2021 gchar
*identity_uid
;
2023 table
= e_msg_composer_get_header_table (composer
);
2024 identity_uid
= e_composer_header_table_dup_identity_uid (table
, NULL
, NULL
);
2025 source
= e_composer_header_table_ref_source (table
, identity_uid
);
2028 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_OPENPGP
)) {
2031 key_id
= e_source_openpgp_dup_key_id (e_source_get_extension (source
, E_SOURCE_EXTENSION_OPENPGP
));
2033 if (key_id
&& *key_id
) {
2034 action
= ACTION (TOOLBAR_PGP_SIGN
);
2035 gtk_action_set_visible (action
, TRUE
);
2037 action
= ACTION (TOOLBAR_PGP_ENCRYPT
);
2038 gtk_action_set_visible (action
, TRUE
);
2044 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_SMIME
)) {
2045 ESourceSMIME
*smime_extension
;
2048 smime_extension
= e_source_get_extension (source
, E_SOURCE_EXTENSION_SMIME
);
2050 certificate
= e_source_smime_dup_signing_certificate (smime_extension
);
2051 if (certificate
&& *certificate
)
2052 gtk_action_set_visible (ACTION (TOOLBAR_SMIME_SIGN
), TRUE
);
2053 g_free (certificate
);
2055 certificate
= e_source_smime_dup_encryption_certificate (smime_extension
);
2056 if (certificate
&& *certificate
)
2057 gtk_action_set_visible (ACTION (TOOLBAR_SMIME_ENCRYPT
), TRUE
);
2058 g_free (certificate
);
2061 g_clear_object (&source
);
2064 g_free (identity_uid
);
2067 g_clear_object (&settings
);
2071 msg_composer_prepare_for_quit_cb (EShell
*shell
,
2072 EActivity
*activity
,
2073 EMsgComposer
*composer
)
2075 if (e_msg_composer_is_exiting (composer
)) {
2076 /* needs save draft first */
2077 g_object_ref (activity
);
2079 G_OBJECT (composer
), (GWeakNotify
)
2080 g_object_unref
, activity
);
2081 gtk_action_activate (ACTION (SAVE_DRAFT
));
2086 msg_composer_quit_requested_cb (EShell
*shell
,
2087 EShellQuitReason reason
,
2088 EMsgComposer
*composer
)
2090 if (e_msg_composer_is_exiting (composer
)) {
2091 g_signal_handlers_disconnect_by_func (
2092 shell
, msg_composer_quit_requested_cb
, composer
);
2093 g_signal_handlers_disconnect_by_func (
2094 shell
, msg_composer_prepare_for_quit_cb
, composer
);
2095 } else if (!e_msg_composer_can_close (composer
, FALSE
) &&
2096 !e_msg_composer_is_exiting (composer
)) {
2097 e_shell_cancel_quit (shell
);
2102 msg_composer_set_editor (EMsgComposer
*composer
,
2103 EHTMLEditor
*editor
)
2105 g_return_if_fail (E_IS_HTML_EDITOR (editor
));
2106 g_return_if_fail (composer
->priv
->editor
== NULL
);
2108 composer
->priv
->editor
= g_object_ref_sink (editor
);
2112 msg_composer_set_shell (EMsgComposer
*composer
,
2115 g_return_if_fail (E_IS_SHELL (shell
));
2116 g_return_if_fail (composer
->priv
->shell
== NULL
);
2118 composer
->priv
->shell
= shell
;
2120 g_object_add_weak_pointer (
2121 G_OBJECT (shell
), &composer
->priv
->shell
);
2125 msg_composer_set_property (GObject
*object
,
2127 const GValue
*value
,
2130 switch (property_id
) {
2132 msg_composer_set_editor (
2133 E_MSG_COMPOSER (object
),
2134 g_value_get_object (value
));
2137 case PROP_IS_REPLY_OR_FORWARD
:
2138 e_msg_composer_set_is_reply_or_forward (
2139 E_MSG_COMPOSER (object
),
2140 g_value_get_boolean (value
));
2144 msg_composer_set_shell (
2145 E_MSG_COMPOSER (object
),
2146 g_value_get_object (value
));
2150 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2154 msg_composer_get_property (GObject
*object
,
2159 switch (property_id
) {
2161 g_value_set_boolean (
2162 value
, e_msg_composer_is_busy (
2163 E_MSG_COMPOSER (object
)));
2167 g_value_set_object (
2168 value
, e_msg_composer_get_editor (
2169 E_MSG_COMPOSER (object
)));
2172 case PROP_FOCUS_TRACKER
:
2173 g_value_set_object (
2174 value
, e_msg_composer_get_focus_tracker (
2175 E_MSG_COMPOSER (object
)));
2178 case PROP_IS_REPLY_OR_FORWARD
:
2179 g_value_set_boolean (
2180 value
, e_msg_composer_get_is_reply_or_forward (
2181 E_MSG_COMPOSER (object
)));
2185 g_value_set_object (
2186 value
, e_msg_composer_get_shell (
2187 E_MSG_COMPOSER (object
)));
2191 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2195 msg_composer_finalize (GObject
*object
)
2197 EMsgComposer
*composer
= E_MSG_COMPOSER (object
);
2199 e_composer_private_finalize (composer
);
2201 /* Chain up to parent's finalize() method. */
2202 G_OBJECT_CLASS (e_msg_composer_parent_class
)->finalize (object
);
2206 msg_composer_gallery_drag_data_get (GtkIconView
*icon_view
,
2207 GdkDragContext
*context
,
2208 GtkSelectionData
*selection_data
,
2213 GtkCellRenderer
*cell
;
2214 GtkTreeModel
*model
;
2219 if (!gtk_icon_view_get_cursor (icon_view
, &path
, &cell
))
2222 target
= gtk_selection_data_get_target (selection_data
);
2224 model
= gtk_icon_view_get_model (icon_view
);
2225 gtk_tree_model_get_iter (model
, &iter
, path
);
2226 gtk_tree_model_get (model
, &iter
, 1, &str_data
, -1);
2227 gtk_tree_path_free (path
);
2229 /* only supports "text/uri-list" */
2230 gtk_selection_data_set (
2231 selection_data
, target
, 8,
2232 (guchar
*) str_data
, strlen (str_data
));
2237 composer_notify_activity_cb (EActivityBar
*activity_bar
,
2239 EMsgComposer
*composer
)
2241 EHTMLEditor
*editor
;
2242 EContentEditor
*cnt_editor
;
2243 gboolean editable
= TRUE
;
2246 busy
= (e_activity_bar_get_activity (activity_bar
) != NULL
);
2248 if (busy
== composer
->priv
->busy
)
2251 composer
->priv
->busy
= busy
;
2254 e_msg_composer_save_focused_widget (composer
);
2256 editor
= e_msg_composer_get_editor (composer
);
2257 cnt_editor
= e_html_editor_get_content_editor (editor
);
2260 editable
= e_content_editor_is_editable (cnt_editor
);
2261 e_content_editor_set_editable (cnt_editor
, FALSE
);
2262 composer
->priv
->saved_editable
= editable
;
2264 editable
= composer
->priv
->saved_editable
;
2265 e_content_editor_set_editable (cnt_editor
, editable
);
2268 g_object_notify (G_OBJECT (composer
), "busy");
2271 e_msg_composer_restore_focus_on_composer (composer
);
2275 msg_composer_constructed (GObject
*object
)
2278 EMsgComposer
*composer
;
2279 EActivityBar
*activity_bar
;
2280 EAttachmentView
*attachment_view
;
2281 EAttachmentStore
*store
;
2282 EComposerHeaderTable
*table
;
2283 EHTMLEditor
*editor
;
2284 EContentEditor
*cnt_editor
;
2285 GtkUIManager
*ui_manager
;
2286 GtkToggleAction
*action
;
2287 GtkTargetList
*target_list
;
2288 GtkTargetEntry
*targets
;
2290 GSettings
*settings
;
2294 /* Chain up to parent's constructed() method. */
2295 G_OBJECT_CLASS (e_msg_composer_parent_class
)->constructed (object
);
2297 composer
= E_MSG_COMPOSER (object
);
2299 g_return_if_fail (E_IS_HTML_EDITOR (composer
->priv
->editor
));
2301 shell
= e_msg_composer_get_shell (composer
);
2303 e_composer_private_constructed (composer
);
2305 editor
= e_msg_composer_get_editor (composer
);
2306 cnt_editor
= e_html_editor_get_content_editor (editor
);
2307 ui_manager
= e_html_editor_get_ui_manager (editor
);
2308 attachment_view
= e_msg_composer_get_attachment_view (composer
);
2309 table
= E_COMPOSER_HEADER_TABLE (composer
->priv
->header_table
);
2311 gtk_window_set_title (GTK_WINDOW (composer
), _("Compose Message"));
2312 gtk_window_set_icon_name (GTK_WINDOW (composer
), "mail-message-new");
2313 gtk_window_set_default_size (GTK_WINDOW (composer
), 600, 500);
2316 object
, "delete-event",
2317 G_CALLBACK (msg_composer_delete_event_cb
), NULL
);
2321 G_CALLBACK (msg_composer_realize_cb
), NULL
);
2323 gtk_application_add_window (
2324 GTK_APPLICATION (shell
), GTK_WINDOW (object
));
2327 shell
, "quit-requested",
2328 G_CALLBACK (msg_composer_quit_requested_cb
), composer
);
2331 shell
, "prepare-for-quit",
2332 G_CALLBACK (msg_composer_prepare_for_quit_cb
), composer
);
2334 /* Restore Persistent State */
2337 GTK_WINDOW (composer
),
2338 "/org/gnome/evolution/mail/composer-window/",
2339 E_RESTORE_WINDOW_SIZE
);
2341 activity_bar
= e_html_editor_get_activity_bar (editor
);
2343 activity_bar
, "notify::activity",
2344 G_CALLBACK (composer_notify_activity_cb
), composer
);
2347 /* Honor User Preferences */
2349 /* FIXME This should be an EMsgComposer property. */
2350 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
2351 action
= GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT
));
2352 active
= g_settings_get_boolean (settings
, "composer-request-receipt");
2353 gtk_toggle_action_set_active (action
, active
);
2355 g_object_unref (settings
);
2357 /* Clipboard Support */
2360 cnt_editor
, "paste-clipboard",
2361 G_CALLBACK (msg_composer_paste_clipboard_cb
), composer
);
2364 cnt_editor
, "paste-primary-clipboard",
2365 G_CALLBACK (msg_composer_paste_primary_clipboard_cb
), composer
);
2367 /* Drag-and-Drop Support */
2369 cnt_editor
, "drag-drop",
2370 G_CALLBACK (msg_composer_drag_drop_cb
), composer
);
2373 cnt_editor
, "drag-begin",
2374 G_CALLBACK (msg_composer_drag_begin_cb
), composer
);
2377 cnt_editor
, "drop-handled",
2378 G_CALLBACK (msg_composer_drop_handled_cb
), composer
);
2381 composer
->priv
->gallery_icon_view
, "drag-data-get",
2382 G_CALLBACK (msg_composer_gallery_drag_data_get
), NULL
);
2384 /* Configure Headers */
2386 composer
->priv
->notify_destinations_bcc_handler
= e_signal_connect_notify_swapped (
2387 table
, "notify::destinations-bcc",
2388 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2389 composer
->priv
->notify_destinations_cc_handler
= e_signal_connect_notify_swapped (
2390 table
, "notify::destinations-cc",
2391 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2392 composer
->priv
->notify_destinations_to_handler
= e_signal_connect_notify_swapped (
2393 table
, "notify::destinations-to",
2394 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2395 /* Do not use e_signal_connect_notify_swapped() here, it it avoids notification
2396 when the property didn't change, but it's about the consolidated property,
2397 identity uid, name and address, where only one of the three can change. */
2398 composer
->priv
->notify_identity_uid_handler
= g_signal_connect_swapped (
2399 table
, "notify::identity-uid",
2400 G_CALLBACK (msg_composer_mail_identity_changed_cb
), composer
);
2401 composer
->priv
->notify_reply_to_handler
= e_signal_connect_notify_swapped (
2402 table
, "notify::reply-to",
2403 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2404 composer
->priv
->notify_signature_uid_handler
= e_signal_connect_notify_swapped (
2405 table
, "notify::signature-uid",
2406 G_CALLBACK (e_composer_update_signature
), composer
);
2407 composer
->priv
->notify_subject_changed_handler
= e_signal_connect_notify_swapped (
2408 table
, "notify::subject",
2409 G_CALLBACK (msg_composer_subject_changed_cb
), composer
);
2410 composer
->priv
->notify_subject_handler
= e_signal_connect_notify_swapped (
2411 table
, "notify::subject",
2412 G_CALLBACK (msg_composer_notify_header_cb
), composer
);
2414 msg_composer_mail_identity_changed_cb (composer
);
2418 store
= e_attachment_view_get_store (attachment_view
);
2420 g_signal_connect_swapped (
2421 store
, "row-deleted",
2422 G_CALLBACK (attachment_store_changed_cb
), composer
);
2424 g_signal_connect_swapped (
2425 store
, "row-inserted",
2426 G_CALLBACK (attachment_store_changed_cb
), composer
);
2428 /* Initialization may have tripped the "changed" state. */
2429 e_content_editor_set_changed (cnt_editor
, FALSE
);
2431 target_list
= e_attachment_view_get_target_list (attachment_view
);
2432 targets
= gtk_target_table_new_from_list (target_list
, &n_targets
);
2434 target_list
= gtk_drag_dest_get_target_list (GTK_WIDGET (cnt_editor
));
2436 gtk_target_list_add_table (target_list
, drag_dest_targets
, G_N_ELEMENTS (drag_dest_targets
));
2437 gtk_target_list_add_table (target_list
, targets
, n_targets
);
2439 gtk_target_table_free (targets
, n_targets
);
2441 id
= "org.gnome.evolution.composer";
2442 e_plugin_ui_register_manager (ui_manager
, id
, composer
);
2443 e_plugin_ui_enable_manager (ui_manager
, id
);
2445 e_extensible_load_extensions (E_EXTENSIBLE (composer
));
2447 e_msg_composer_set_body_text (composer
, "", TRUE
);
2451 msg_composer_dispose (GObject
*object
)
2453 EMsgComposer
*composer
= E_MSG_COMPOSER (object
);
2454 EMsgComposerPrivate
*priv
= E_MSG_COMPOSER_GET_PRIVATE (composer
);
2457 if (priv
->address_dialog
!= NULL
) {
2458 gtk_widget_destroy (priv
->address_dialog
);
2459 priv
->address_dialog
= NULL
;
2462 /* FIXME Our EShell is already unreferenced. */
2463 shell
= e_shell_get_default ();
2465 g_signal_handlers_disconnect_by_func (
2466 shell
, msg_composer_quit_requested_cb
, composer
);
2467 g_signal_handlers_disconnect_by_func (
2468 shell
, msg_composer_prepare_for_quit_cb
, composer
);
2470 if (priv
->header_table
!= NULL
) {
2471 EComposerHeaderTable
*table
;
2473 table
= E_COMPOSER_HEADER_TABLE (composer
->priv
->header_table
);
2475 e_signal_disconnect_notify_handler (
2476 table
, &priv
->notify_destinations_bcc_handler
);
2477 e_signal_disconnect_notify_handler (
2478 table
, &priv
->notify_destinations_cc_handler
);
2479 e_signal_disconnect_notify_handler (
2480 table
, &priv
->notify_destinations_to_handler
);
2481 e_signal_disconnect_notify_handler (
2482 table
, &priv
->notify_identity_uid_handler
);
2483 e_signal_disconnect_notify_handler (
2484 table
, &priv
->notify_reply_to_handler
);
2485 e_signal_disconnect_notify_handler (
2486 table
, &priv
->notify_destinations_to_handler
);
2487 e_signal_disconnect_notify_handler (
2488 table
, &priv
->notify_subject_changed_handler
);
2491 e_composer_private_dispose (composer
);
2493 /* Chain up to parent's dispose() method. */
2494 G_OBJECT_CLASS (e_msg_composer_parent_class
)->dispose (object
);
2498 msg_composer_map (GtkWidget
*widget
)
2500 EMsgComposer
*composer
;
2501 EComposerHeaderTable
*table
;
2502 GtkWidget
*input_widget
;
2503 EHTMLEditor
*editor
;
2504 EContentEditor
*cnt_editor
;
2507 /* Chain up to parent's map() method. */
2508 GTK_WIDGET_CLASS (e_msg_composer_parent_class
)->map (widget
);
2510 composer
= E_MSG_COMPOSER (widget
);
2511 editor
= e_msg_composer_get_editor (composer
);
2512 table
= e_msg_composer_get_header_table (composer
);
2514 /* If the 'To' field is empty, focus it. */
2516 e_composer_header_table_get_header (
2517 table
, E_COMPOSER_HEADER_TO
)->input_widget
;
2518 text
= gtk_entry_get_text (GTK_ENTRY (input_widget
));
2519 if (gtk_widget_get_visible (input_widget
) && (text
== NULL
|| *text
== '\0')) {
2520 gtk_widget_grab_focus (input_widget
);
2524 /* If not, check the 'Subject' field. */
2526 e_composer_header_table_get_header (
2527 table
, E_COMPOSER_HEADER_SUBJECT
)->input_widget
;
2528 text
= gtk_entry_get_text (GTK_ENTRY (input_widget
));
2529 if (gtk_widget_get_visible (input_widget
) && (text
== NULL
|| *text
== '\0')) {
2530 gtk_widget_grab_focus (input_widget
);
2534 /* Jump to the editor as a last resort. */
2535 cnt_editor
= e_html_editor_get_content_editor (editor
);
2536 gtk_widget_grab_focus (GTK_WIDGET (cnt_editor
));
2540 msg_composer_key_press_event (GtkWidget
*widget
,
2543 EMsgComposer
*composer
;
2544 GtkWidget
*input_widget
;
2545 EHTMLEditor
*editor
;
2546 EContentEditor
*cnt_editor
;
2548 composer
= E_MSG_COMPOSER (widget
);
2549 editor
= e_msg_composer_get_editor (composer
);
2550 cnt_editor
= e_html_editor_get_content_editor (editor
);
2553 e_composer_header_table_get_header (
2554 e_msg_composer_get_header_table (composer
),
2555 E_COMPOSER_HEADER_SUBJECT
)->input_widget
;
2558 if (event
->keyval
== XF86XK_Send
) {
2559 e_msg_composer_send (composer
);
2562 #endif /* HAVE_XFREE */
2564 if (event
->keyval
== GDK_KEY_Escape
) {
2565 gtk_action_activate (ACTION (CLOSE
));
2569 if (event
->keyval
== GDK_KEY_Tab
&& gtk_widget_is_focus (input_widget
)) {
2570 gtk_widget_grab_focus (GTK_WIDGET (cnt_editor
));
2574 if (gtk_widget_is_focus (GTK_WIDGET (cnt_editor
))) {
2575 if (event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
2576 gboolean view_processed
= FALSE
;
2578 g_signal_emit_by_name (cnt_editor
, "key-press-event", event
, &view_processed
);
2580 if (!view_processed
)
2581 gtk_widget_grab_focus (input_widget
);
2587 if (e_util_check_gtk_bindings_in_key_press_event_cb (widget
, (GdkEvent
*) event
))
2590 /* Chain up to parent's key_press_event() method. */
2591 return GTK_WIDGET_CLASS (e_msg_composer_parent_class
)->
2592 key_press_event (widget
, event
);
2596 msg_composer_presend (EMsgComposer
*composer
)
2598 /* This keeps the signal accumulator at TRUE. */
2603 msg_composer_accumulator_false_abort (GSignalInvocationHint
*ihint
,
2604 GValue
*return_accu
,
2605 const GValue
*handler_return
,
2610 v_boolean
= g_value_get_boolean (handler_return
);
2611 g_value_set_boolean (return_accu
, v_boolean
);
2613 /* FALSE means abort the signal emission. */
2618 * e_msg_composer_is_busy:
2619 * @composer: an #EMsgComposer
2621 * Returns %TRUE only while an #EActivity is in progress.
2623 * Returns: whether @composer is busy
2626 e_msg_composer_is_busy (EMsgComposer
*composer
)
2628 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), FALSE
);
2630 return composer
->priv
->busy
;
2634 e_msg_composer_class_init (EMsgComposerClass
*class)
2636 GObjectClass
*object_class
;
2637 GtkWidgetClass
*widget_class
;
2639 g_type_class_add_private (class, sizeof (EMsgComposerPrivate
));
2641 object_class
= G_OBJECT_CLASS (class);
2642 object_class
->set_property
= msg_composer_set_property
;
2643 object_class
->get_property
= msg_composer_get_property
;
2644 object_class
->dispose
= msg_composer_dispose
;
2645 object_class
->finalize
= msg_composer_finalize
;
2646 object_class
->constructed
= msg_composer_constructed
;
2648 widget_class
= GTK_WIDGET_CLASS (class);
2649 widget_class
->map
= msg_composer_map
;
2650 widget_class
->key_press_event
= msg_composer_key_press_event
;
2652 class->presend
= msg_composer_presend
;
2654 g_object_class_install_property (
2657 g_param_spec_boolean (
2660 "Whether an activity is in progress",
2663 G_PARAM_STATIC_STRINGS
));
2665 g_object_class_install_property (
2668 g_param_spec_object (
2674 G_PARAM_CONSTRUCT_ONLY
));
2676 g_object_class_install_property (
2679 g_param_spec_object (
2683 E_TYPE_FOCUS_TRACKER
,
2686 g_object_class_install_property (
2688 PROP_IS_REPLY_OR_FORWARD
,
2689 g_param_spec_boolean (
2690 "is-reply-or-forward",
2691 "Is Reply Or Forward",
2692 "Whether the composed message is a reply or a forward message",
2695 G_PARAM_STATIC_STRINGS
));
2697 g_object_class_install_property (
2700 g_param_spec_object (
2703 "The EShell singleton",
2706 G_PARAM_CONSTRUCT_ONLY
));
2708 signals
[PRESEND
] = g_signal_new (
2710 G_OBJECT_CLASS_TYPE (class),
2712 G_STRUCT_OFFSET (EMsgComposerClass
, presend
),
2713 msg_composer_accumulator_false_abort
,
2715 e_marshal_BOOLEAN__VOID
,
2718 signals
[SEND
] = g_signal_new (
2720 G_OBJECT_CLASS_TYPE (class),
2722 G_STRUCT_OFFSET (EMsgComposerClass
, send
),
2724 e_marshal_VOID__OBJECT_OBJECT
,
2726 CAMEL_TYPE_MIME_MESSAGE
,
2729 signals
[SAVE_TO_DRAFTS
] = g_signal_new (
2731 G_OBJECT_CLASS_TYPE (class),
2733 G_STRUCT_OFFSET (EMsgComposerClass
, save_to_drafts
),
2735 e_marshal_VOID__OBJECT_OBJECT
,
2737 CAMEL_TYPE_MIME_MESSAGE
,
2740 signals
[SAVE_TO_OUTBOX
] = g_signal_new (
2742 G_OBJECT_CLASS_TYPE (class),
2744 G_STRUCT_OFFSET (EMsgComposerClass
, save_to_outbox
),
2746 e_marshal_VOID__OBJECT_OBJECT
,
2748 CAMEL_TYPE_MIME_MESSAGE
,
2751 signals
[PRINT
] = g_signal_new (
2753 G_OBJECT_CLASS_TYPE (class),
2756 e_marshal_VOID__ENUM_OBJECT_OBJECT
,
2758 GTK_TYPE_PRINT_OPERATION_ACTION
,
2759 CAMEL_TYPE_MIME_MESSAGE
,
2762 signals
[BEFORE_DESTROY
] = g_signal_new (
2764 G_OBJECT_CLASS_TYPE (class),
2767 g_cclosure_marshal_VOID__VOID
,
2773 e_composer_emit_before_destroy (EMsgComposer
*composer
)
2775 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2777 g_signal_emit (composer
, signals
[BEFORE_DESTROY
], 0);
2781 e_msg_composer_init (EMsgComposer
*composer
)
2783 composer
->priv
= E_MSG_COMPOSER_GET_PRIVATE (composer
);
2787 e_msg_composer_editor_created_cb (GObject
*source_object
,
2788 GAsyncResult
*result
,
2792 ESimpleAsyncResult
*eresult
= user_data
;
2793 GError
*error
= NULL
;
2795 g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (eresult
));
2797 editor
= e_html_editor_new_finish (result
, &error
);
2799 g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC
, error
->message
);
2800 g_clear_error (&error
);
2802 e_simple_async_result_set_op_pointer (eresult
, editor
, NULL
);
2803 e_simple_async_result_complete (eresult
);
2806 g_object_unref (eresult
);
2810 * e_msg_composer_new:
2811 * @shell: an #EShell
2812 * @callback: called when the composer is ready
2813 * @user_data: user data passed to @callback
2815 * Asynchronously creates an #EMsgComposer. The operation is finished
2816 * with e_msg_composer_new_finish() called from within the @callback.
2821 e_msg_composer_new (EShell
*shell
,
2822 GAsyncReadyCallback callback
,
2825 ESimpleAsyncResult
*eresult
;
2827 g_return_if_fail (E_IS_SHELL (shell
));
2828 g_return_if_fail (callback
!= NULL
);
2830 eresult
= e_simple_async_result_new (NULL
, callback
, user_data
, e_msg_composer_new
);
2831 e_simple_async_result_set_user_data (eresult
, g_object_ref (shell
), g_object_unref
);
2833 e_html_editor_new (e_msg_composer_editor_created_cb
, eresult
);
2837 * e_msg_composer_new_finish:
2838 * @result: a #GAsyncResult provided by the callback from e_msg_composer_new()
2839 * @error: optional #GError for errors
2841 * Finishes call of e_msg_composer_new().
2846 e_msg_composer_new_finish (GAsyncResult
*result
,
2849 ESimpleAsyncResult
*eresult
;
2850 EHTMLEditor
*html_editor
;
2852 g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result
), NULL
);
2853 g_return_val_if_fail (g_async_result_is_tagged (result
, e_msg_composer_new
), NULL
);
2855 eresult
= E_SIMPLE_ASYNC_RESULT (result
);
2857 html_editor
= e_simple_async_result_get_op_pointer (eresult
);
2858 g_return_val_if_fail (E_IS_HTML_EDITOR (html_editor
), NULL
);
2860 return g_object_new (E_TYPE_MSG_COMPOSER
,
2861 "shell", e_simple_async_result_get_user_data (eresult
),
2862 "editor", html_editor
,
2867 * e_msg_composer_get_editor:
2868 * @composer: an #EMsgComposer
2870 * Returns @composer's internal #EHTMLEditor instance.
2872 * Returns: an #EHTMLEditor
2875 e_msg_composer_get_editor (EMsgComposer
*composer
)
2877 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
2879 return composer
->priv
->editor
;
2883 e_msg_composer_get_focus_tracker (EMsgComposer
*composer
)
2885 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
2887 return composer
->priv
->focus_tracker
;
2891 e_msg_composer_set_pending_body (EMsgComposer
*composer
,
2896 g_object_set_data_full (
2897 G_OBJECT (composer
), "body:text_mime_type",
2898 GINT_TO_POINTER (is_html
), NULL
);
2899 g_object_set_data_full (
2900 G_OBJECT (composer
), "body:text",
2901 text
, (GDestroyNotify
) g_free
);
2905 e_msg_composer_flush_pending_body (EMsgComposer
*composer
)
2910 body
= g_object_get_data (G_OBJECT (composer
), "body:text");
2911 is_html
= GPOINTER_TO_INT (
2912 g_object_get_data (G_OBJECT (composer
), "body:text_mime_type"));
2915 set_editor_text (composer
, body
, is_html
, FALSE
);
2917 g_object_set_data (G_OBJECT (composer
), "body:text", NULL
);
2921 add_attachments_handle_mime_part (EMsgComposer
*composer
,
2922 CamelMimePart
*mime_part
,
2923 gboolean just_inlines
,
2927 CamelContentType
*content_type
;
2928 CamelDataWrapper
*wrapper
;
2929 EHTMLEditor
*editor
;
2930 EContentEditor
*cnt_editor
;
2935 content_type
= camel_mime_part_get_content_type (mime_part
);
2936 wrapper
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
2937 editor
= e_msg_composer_get_editor (composer
);
2938 cnt_editor
= e_html_editor_get_content_editor (editor
);
2940 if (CAMEL_IS_MULTIPART (wrapper
)) {
2941 /* another layer of multipartness... */
2942 add_attachments_from_multipart (
2943 composer
, (CamelMultipart
*) wrapper
,
2944 just_inlines
, depth
+ 1);
2945 } else if (just_inlines
) {
2946 if (camel_mime_part_get_content_id (mime_part
) ||
2947 camel_mime_part_get_content_location (mime_part
))
2948 e_content_editor_insert_image_from_mime_part (
2949 cnt_editor
, mime_part
);
2950 } else if (related
&& camel_content_type_is (content_type
, "image", "*")) {
2951 e_content_editor_insert_image_from_mime_part (cnt_editor
, mime_part
);
2952 } else if (camel_content_type_is (content_type
, "text", "*") &&
2953 camel_mime_part_get_filename (mime_part
) == NULL
) {
2954 /* Do nothing if this is a text/anything without a
2955 * filename, otherwise attach it too. */
2957 e_msg_composer_attach (composer
, mime_part
);
2962 add_attachments_from_multipart (EMsgComposer
*composer
,
2963 CamelMultipart
*multipart
,
2964 gboolean just_inlines
,
2967 /* find appropriate message attachments to add to the composer */
2968 CamelMimePart
*mime_part
;
2972 related
= camel_content_type_is (
2973 camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (multipart
)),
2974 "multipart", "related");
2976 if (CAMEL_IS_MULTIPART_SIGNED (multipart
)) {
2977 mime_part
= camel_multipart_get_part (
2978 multipart
, CAMEL_MULTIPART_SIGNED_CONTENT
);
2979 add_attachments_handle_mime_part (
2980 composer
, mime_part
, just_inlines
, related
, depth
);
2981 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (multipart
)) {
2982 /* XXX What should we do in this case? */
2984 nparts
= camel_multipart_get_number (multipart
);
2986 for (i
= 0; i
< nparts
; i
++) {
2987 mime_part
= camel_multipart_get_part (multipart
, i
);
2988 add_attachments_handle_mime_part (
2989 composer
, mime_part
, just_inlines
,
2996 * e_msg_composer_add_message_attachments:
2997 * @composer: the composer to add the attachments to.
2998 * @message: the source message to copy the attachments from.
2999 * @just_inlines: whether to attach all attachments or just add
3002 * Walk through all the mime parts in @message and add them to the composer
3003 * specified in @composer.
3006 e_msg_composer_add_message_attachments (EMsgComposer
*composer
,
3007 CamelMimeMessage
*message
,
3008 gboolean just_inlines
)
3010 CamelDataWrapper
*wrapper
;
3012 wrapper
= camel_medium_get_content (CAMEL_MEDIUM (message
));
3013 if (!CAMEL_IS_MULTIPART (wrapper
))
3016 add_attachments_from_multipart (
3017 composer
, (CamelMultipart
*) wrapper
, just_inlines
, 0);
3021 handle_multipart_signed (EMsgComposer
*composer
,
3022 CamelMultipart
*multipart
,
3023 gboolean keep_signature
,
3024 GCancellable
*cancellable
,
3027 CamelContentType
*content_type
;
3028 CamelDataWrapper
*content
;
3029 CamelMimePart
*mime_part
;
3030 GtkToggleAction
*action
= NULL
;
3031 const gchar
*protocol
;
3033 content
= CAMEL_DATA_WRAPPER (multipart
);
3034 content_type
= camel_data_wrapper_get_mime_type_field (content
);
3035 protocol
= camel_content_type_param (content_type
, "protocol");
3037 if (protocol
== NULL
) {
3039 } else if (g_ascii_strcasecmp (protocol
, "application/pgp-signature") == 0) {
3040 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
))) &&
3041 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
))))
3042 action
= GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
));
3043 } else if (g_ascii_strcasecmp (protocol
, "application/x-pkcs7-signature") == 0) {
3044 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
))) &&
3045 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
))))
3046 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
));
3050 gtk_toggle_action_set_active (action
, TRUE
);
3052 mime_part
= camel_multipart_get_part (
3053 multipart
, CAMEL_MULTIPART_SIGNED_CONTENT
);
3055 if (mime_part
== NULL
)
3058 content_type
= camel_mime_part_get_content_type (mime_part
);
3059 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
3061 if (CAMEL_IS_MULTIPART (content
)) {
3062 multipart
= CAMEL_MULTIPART (content
);
3064 /* Note: depth is preserved here because we're not
3065 * counting multipart/signed as a multipart, instead
3066 * we want to treat the content part as our mime part
3069 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3070 /* Handle the signed content and configure
3071 * the composer to sign outgoing messages. */
3072 handle_multipart_signed (
3073 composer
, multipart
, keep_signature
, cancellable
, depth
);
3075 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3076 /* Decrypt the encrypted content and configure
3077 * the composer to encrypt outgoing messages. */
3078 handle_multipart_encrypted (
3079 composer
, mime_part
, keep_signature
, cancellable
, depth
);
3081 } else if (camel_content_type_is (content_type
, "multipart", "alternative")) {
3082 /* This contains the text/plain and text/html
3083 * versions of the message body. */
3084 handle_multipart_alternative (
3085 composer
, multipart
, keep_signature
, cancellable
, depth
);
3088 /* There must be attachments... */
3090 composer
, multipart
, keep_signature
, cancellable
, depth
);
3093 } else if (camel_content_type_is (content_type
, "text", "*")) {
3097 html
= emcu_part_to_html (
3098 composer
, mime_part
, &length
, keep_signature
, cancellable
);
3100 e_msg_composer_set_pending_body (composer
, html
, length
, TRUE
);
3103 e_msg_composer_attach (composer
, mime_part
);
3108 handle_multipart_encrypted (EMsgComposer
*composer
,
3109 CamelMimePart
*multipart
,
3110 gboolean keep_signature
,
3111 GCancellable
*cancellable
,
3114 CamelContentType
*content_type
;
3115 CamelCipherContext
*cipher
;
3116 CamelDataWrapper
*content
;
3117 CamelMimePart
*mime_part
;
3118 CamelSession
*session
;
3119 CamelCipherValidity
*valid
;
3120 GtkToggleAction
*action
= NULL
;
3121 const gchar
*protocol
;
3123 content_type
= camel_mime_part_get_content_type (multipart
);
3124 protocol
= camel_content_type_param (content_type
, "protocol");
3126 if (protocol
&& g_ascii_strcasecmp (protocol
, "application/pgp-encrypted") == 0) {
3127 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
))) &&
3128 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
))))
3129 action
= GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
));
3130 } else if (content_type
&& (
3131 camel_content_type_is (content_type
, "application", "x-pkcs7-mime")
3132 || camel_content_type_is (content_type
, "application", "pkcs7-mime"))) {
3133 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
))) &&
3134 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
))))
3135 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
));
3139 gtk_toggle_action_set_active (action
, TRUE
);
3141 session
= e_msg_composer_ref_session (composer
);
3142 cipher
= camel_gpg_context_new (session
);
3143 mime_part
= camel_mime_part_new ();
3144 valid
= camel_cipher_context_decrypt_sync (
3145 cipher
, multipart
, mime_part
, cancellable
, NULL
);
3146 g_object_unref (cipher
);
3147 g_object_unref (session
);
3152 camel_cipher_validity_free (valid
);
3154 content_type
= camel_mime_part_get_content_type (mime_part
);
3156 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
3158 if (CAMEL_IS_MULTIPART (content
)) {
3159 CamelMultipart
*content_multipart
= CAMEL_MULTIPART (content
);
3161 /* Note: depth is preserved here because we're not
3162 * counting multipart/encrypted as a multipart, instead
3163 * we want to treat the content part as our mime part
3166 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3167 /* Handle the signed content and configure the
3168 * composer to sign outgoing messages. */
3169 handle_multipart_signed (
3170 composer
, content_multipart
, keep_signature
, cancellable
, depth
);
3172 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3173 /* Decrypt the encrypted content and configure the
3174 * composer to encrypt outgoing messages. */
3175 handle_multipart_encrypted (
3176 composer
, mime_part
, keep_signature
, cancellable
, depth
);
3178 } else if (camel_content_type_is (content_type
, "multipart", "alternative")) {
3179 /* This contains the text/plain and text/html
3180 * versions of the message body. */
3181 handle_multipart_alternative (
3182 composer
, content_multipart
, keep_signature
, cancellable
, depth
);
3185 /* There must be attachments... */
3187 composer
, content_multipart
, keep_signature
, cancellable
, depth
);
3190 } else if (camel_content_type_is (content_type
, "text", "*")) {
3194 html
= emcu_part_to_html (
3195 composer
, mime_part
, &length
, keep_signature
, cancellable
);
3197 e_msg_composer_set_pending_body (composer
, html
, length
, TRUE
);
3200 e_msg_composer_attach (composer
, mime_part
);
3203 g_object_unref (mime_part
);
3207 handle_multipart_alternative (EMsgComposer
*composer
,
3208 CamelMultipart
*multipart
,
3209 gboolean keep_signature
,
3210 GCancellable
*cancellable
,
3213 /* Find the text/html part and set the composer body to its content */
3214 CamelMimePart
*text_part
= NULL
, *fallback_text_part
= NULL
;
3217 nparts
= camel_multipart_get_number (multipart
);
3219 for (i
= 0; i
< nparts
; i
++) {
3220 CamelContentType
*content_type
;
3221 CamelDataWrapper
*content
;
3222 CamelMimePart
*mime_part
;
3224 mime_part
= camel_multipart_get_part (multipart
, i
);
3229 content_type
= camel_mime_part_get_content_type (mime_part
);
3230 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
3232 if (CAMEL_IS_MULTIPART (content
)) {
3235 mp
= CAMEL_MULTIPART (content
);
3237 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3238 /* Handle the signed content and configure
3239 * the composer to sign outgoing messages. */
3240 handle_multipart_signed (
3241 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3243 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3244 /* Decrypt the encrypted content and configure
3245 * the composer to encrypt outgoing messages. */
3246 handle_multipart_encrypted (
3247 composer
, mime_part
, keep_signature
,
3248 cancellable
, depth
+ 1);
3251 /* Depth doesn't matter so long as we
3254 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3257 } else if (camel_content_type_is (content_type
, "text", "html")) {
3258 /* text/html is preferable, so once we find it we're done... */
3259 text_part
= mime_part
;
3261 } else if (camel_content_type_is (content_type
, "text", "*")) {
3262 /* anyt text part not text/html is second rate so the first
3263 * text part we find isn't necessarily the one we'll use. */
3265 text_part
= mime_part
;
3267 /* this is when prefer-plain filters out text/html part, then
3268 * the text/plain should be used */
3269 if (camel_content_type_is (content_type
, "text", "plain"))
3270 fallback_text_part
= mime_part
;
3272 e_msg_composer_attach (composer
, mime_part
);
3280 html
= emcu_part_to_html (
3281 composer
, text_part
, &length
, keep_signature
, cancellable
);
3282 if (!html
&& fallback_text_part
)
3283 html
= emcu_part_to_html (
3284 composer
, fallback_text_part
, &length
, keep_signature
, cancellable
);
3286 e_msg_composer_set_pending_body (composer
, html
, length
, TRUE
);
3291 handle_multipart (EMsgComposer
*composer
,
3292 CamelMultipart
*multipart
,
3293 gboolean keep_signature
,
3294 GCancellable
*cancellable
,
3299 nparts
= camel_multipart_get_number (multipart
);
3301 for (i
= 0; i
< nparts
; i
++) {
3302 CamelContentType
*content_type
;
3303 CamelDataWrapper
*content
;
3304 CamelMimePart
*mime_part
;
3306 mime_part
= camel_multipart_get_part (multipart
, i
);
3311 content_type
= camel_mime_part_get_content_type (mime_part
);
3312 content
= camel_medium_get_content (CAMEL_MEDIUM (mime_part
));
3314 if (CAMEL_IS_MULTIPART (content
)) {
3317 mp
= CAMEL_MULTIPART (content
);
3319 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3320 /* Handle the signed content and configure
3321 * the composer to sign outgoing messages. */
3322 handle_multipart_signed (
3323 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3325 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3326 /* Decrypt the encrypted content and configure
3327 * the composer to encrypt outgoing messages. */
3328 handle_multipart_encrypted (
3329 composer
, mime_part
, keep_signature
,
3330 cancellable
, depth
+ 1);
3332 } else if (camel_content_type_is (
3333 content_type
, "multipart", "alternative")) {
3334 handle_multipart_alternative (
3335 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3338 /* Depth doesn't matter so long as we
3341 composer
, mp
, keep_signature
, cancellable
, depth
+ 1);
3344 } else if (depth
== 0 && i
== 0) {
3348 /* Since the first part is not multipart/alternative,
3349 * this must be the body. */
3350 html
= emcu_part_to_html (
3351 composer
, mime_part
, &length
, keep_signature
, cancellable
);
3353 e_msg_composer_set_pending_body (composer
, html
, length
, TRUE
);
3355 } else if (camel_mime_part_get_content_id (mime_part
) ||
3356 camel_mime_part_get_content_location (mime_part
)) {
3357 /* special in-line attachment */
3358 EHTMLEditor
*editor
;
3359 EContentEditor
*cnt_editor
;
3361 editor
= e_msg_composer_get_editor (composer
);
3362 cnt_editor
= e_html_editor_get_content_editor (editor
);
3364 e_content_editor_insert_image_from_mime_part (cnt_editor
, mime_part
);
3366 /* normal attachment */
3367 e_msg_composer_attach (composer
, mime_part
);
3373 set_signature_gui (EMsgComposer
*composer
)
3375 EHTMLEditor
*editor
;
3376 EContentEditor
*cnt_editor
;
3377 EComposerHeaderTable
*table
;
3378 EMailSignatureComboBox
*combo_box
;
3381 table
= e_msg_composer_get_header_table (composer
);
3382 combo_box
= e_composer_header_table_get_signature_combo_box (table
);
3384 editor
= e_msg_composer_get_editor (composer
);
3385 cnt_editor
= e_html_editor_get_content_editor (editor
);
3387 if ((uid
= e_content_editor_get_current_signature_uid (cnt_editor
))) {
3388 /* The combo box active ID is the signature's ESource UID. */
3389 gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box
), uid
);
3394 composer_add_auto_recipients (ESource
*source
,
3395 const gchar
*property_name
,
3396 GHashTable
*hash_table
)
3398 ESourceMailComposition
*extension
;
3399 CamelInternetAddress
*inet_addr
;
3400 const gchar
*extension_name
;
3401 gchar
*comma_separated_addrs
;
3402 gchar
**addr_array
= NULL
;
3406 extension_name
= E_SOURCE_EXTENSION_MAIL_COMPOSITION
;
3407 extension
= e_source_get_extension (source
, extension_name
);
3409 g_object_get (extension
, property_name
, &addr_array
, NULL
);
3411 if (addr_array
== NULL
)
3414 inet_addr
= camel_internet_address_new ();
3415 comma_separated_addrs
= g_strjoinv (", ", addr_array
);
3417 retval
= camel_address_decode (
3418 CAMEL_ADDRESS (inet_addr
), comma_separated_addrs
);
3420 g_free (comma_separated_addrs
);
3421 g_strfreev (addr_array
);
3426 length
= camel_address_length (CAMEL_ADDRESS (inet_addr
));
3428 for (ii
= 0; ii
< length
; ii
++) {
3432 if (camel_internet_address_get (inet_addr
, ii
, &name
, &addr
))
3433 g_hash_table_add (hash_table
, g_strdup (addr
));
3436 g_object_unref (inet_addr
);
3440 * e_msg_composer_setup_with_message:
3441 * @composer: an #EMsgComposer
3442 * @message: The message to use as the source
3443 * @keep_signature: Keep message signature, if any
3444 * @override_identity_uid: (allow none): Optional identity UID to use, or %NULL
3445 * @override_alias_name: (nullable): an alias name to use together with the override_identity_uid, or %NULL
3446 * @override_alias_address: (nullable): an alias address to use together with the override_identity_uid, or %NULL
3447 * @cancellable: optional #GCancellable object, or %NULL
3449 * Sets up the message @composer with a specific @message.
3451 * Note: Designed to work only for messages constructed using Evolution.
3456 e_msg_composer_setup_with_message (EMsgComposer
*composer
,
3457 CamelMimeMessage
*message
,
3458 gboolean keep_signature
,
3459 const gchar
*override_identity_uid
,
3460 const gchar
*override_alias_name
,
3461 const gchar
*override_alias_address
,
3462 GCancellable
*cancellable
)
3464 CamelInternetAddress
*from
, *to
, *cc
, *bcc
;
3465 GList
*To
= NULL
, *Cc
= NULL
, *Bcc
= NULL
, *postto
= NULL
;
3466 const gchar
*format
, *subject
, *composer_mode
;
3467 EDestination
**Tov
, **Ccv
, **Bccv
;
3468 GHashTable
*auto_cc
, *auto_bcc
;
3469 CamelContentType
*content_type
;
3470 const CamelNameValueArray
*headers
;
3471 CamelDataWrapper
*content
;
3472 EMsgComposerPrivate
*priv
;
3473 EComposerHeaderTable
*table
;
3474 ESource
*source
= NULL
;
3475 EHTMLEditor
*editor
;
3476 EContentEditor
*cnt_editor
;
3477 GtkToggleAction
*action
;
3478 gchar
*identity_uid
;
3481 gboolean is_message_from_draft
= FALSE
;
3483 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
3485 headers
= camel_medium_get_headers (CAMEL_MEDIUM (message
));
3486 jjlen
= camel_name_value_array_get_length (headers
);
3487 for (jj
= 0; jj
< jjlen
; jj
++) {
3488 const gchar
*header_name
= NULL
, *header_value
= NULL
;
3491 if (!camel_name_value_array_get (headers
, jj
, &header_name
, &header_value
) ||
3495 if (g_ascii_strcasecmp (header_name
, "X-Evolution-PostTo") == 0) {
3496 value
= g_strstrip (g_strdup (header_value
));
3497 postto
= g_list_append (postto
, value
);
3501 priv
= E_MSG_COMPOSER_GET_PRIVATE (composer
);
3502 table
= e_msg_composer_get_header_table (composer
);
3503 editor
= e_msg_composer_get_editor (composer
);
3504 cnt_editor
= e_html_editor_get_content_editor (editor
);
3507 e_composer_header_table_set_post_to_list (table
, postto
);
3508 g_list_foreach (postto
, (GFunc
) g_free
, NULL
);
3509 g_list_free (postto
);
3513 if (override_identity_uid
&& *override_identity_uid
) {
3514 identity_uid
= (gchar
*) override_identity_uid
;
3516 /* Restore the mail identity preference. */
3517 identity_uid
= (gchar
*) camel_medium_get_header (
3518 CAMEL_MEDIUM (message
), "X-Evolution-Identity");
3519 if (!identity_uid
) {
3520 /* for backward compatibility */
3521 identity_uid
= (gchar
*) camel_medium_get_header (
3522 CAMEL_MEDIUM (message
), "X-Evolution-Account");
3524 if (!identity_uid
) {
3525 source
= em_utils_guess_mail_identity_with_recipients (
3526 e_shell_get_registry (e_msg_composer_get_shell (composer
)), message
, NULL
, NULL
, NULL
, NULL
);
3528 identity_uid
= e_source_dup_uid (source
);
3532 if (identity_uid
!= NULL
&& !source
) {
3533 identity_uid
= g_strstrip (g_strdup (identity_uid
));
3534 source
= e_composer_header_table_ref_source (
3535 table
, identity_uid
);
3538 auto_cc
= g_hash_table_new_full (
3539 (GHashFunc
) camel_strcase_hash
,
3540 (GEqualFunc
) camel_strcase_equal
,
3541 (GDestroyNotify
) g_free
,
3542 (GDestroyNotify
) NULL
);
3544 auto_bcc
= g_hash_table_new_full (
3545 (GHashFunc
) camel_strcase_hash
,
3546 (GEqualFunc
) camel_strcase_equal
,
3547 (GDestroyNotify
) g_free
,
3548 (GDestroyNotify
) NULL
);
3550 if (source
!= NULL
) {
3551 composer_add_auto_recipients (source
, "cc", auto_cc
);
3552 composer_add_auto_recipients (source
, "bcc", auto_bcc
);
3555 to
= camel_mime_message_get_recipients (message
, CAMEL_RECIPIENT_TYPE_TO
);
3556 cc
= camel_mime_message_get_recipients (message
, CAMEL_RECIPIENT_TYPE_CC
);
3557 bcc
= camel_mime_message_get_recipients (message
, CAMEL_RECIPIENT_TYPE_BCC
);
3559 len
= camel_address_length (CAMEL_ADDRESS (to
));
3560 for (i
= 0; i
< len
; i
++) {
3561 const gchar
*name
, *addr
;
3563 if (camel_internet_address_get (to
, i
, &name
, &addr
)) {
3564 EDestination
*dest
= e_destination_new ();
3565 e_destination_set_name (dest
, name
);
3566 e_destination_set_email (dest
, addr
);
3567 To
= g_list_append (To
, dest
);
3571 Tov
= destination_list_to_vector (To
);
3574 len
= camel_address_length (CAMEL_ADDRESS (cc
));
3575 for (i
= 0; i
< len
; i
++) {
3576 const gchar
*name
, *addr
;
3578 if (camel_internet_address_get (cc
, i
, &name
, &addr
)) {
3579 EDestination
*dest
= e_destination_new ();
3580 e_destination_set_name (dest
, name
);
3581 e_destination_set_email (dest
, addr
);
3583 if (g_hash_table_contains (auto_cc
, addr
))
3584 e_destination_set_auto_recipient (dest
, TRUE
);
3586 Cc
= g_list_append (Cc
, dest
);
3590 Ccv
= destination_list_to_vector (Cc
);
3591 g_hash_table_destroy (auto_cc
);
3594 len
= camel_address_length (CAMEL_ADDRESS (bcc
));
3595 for (i
= 0; i
< len
; i
++) {
3596 const gchar
*name
, *addr
;
3598 if (camel_internet_address_get (bcc
, i
, &name
, &addr
)) {
3599 EDestination
*dest
= e_destination_new ();
3600 e_destination_set_name (dest
, name
);
3601 e_destination_set_email (dest
, addr
);
3603 if (g_hash_table_contains (auto_bcc
, addr
))
3604 e_destination_set_auto_recipient (dest
, TRUE
);
3606 Bcc
= g_list_append (Bcc
, dest
);
3610 Bccv
= destination_list_to_vector (Bcc
);
3611 g_hash_table_destroy (auto_bcc
);
3615 g_object_unref (source
);
3617 subject
= camel_mime_message_get_subject (message
);
3619 e_composer_header_table_set_destinations_to (table
, Tov
);
3620 e_composer_header_table_set_destinations_cc (table
, Ccv
);
3621 e_composer_header_table_set_destinations_bcc (table
, Bccv
);
3622 e_composer_header_table_set_subject (table
, subject
);
3624 e_destination_freev (Tov
);
3625 e_destination_freev (Ccv
);
3626 e_destination_freev (Bccv
);
3628 from
= camel_mime_message_get_from (message
);
3629 if ((!override_identity_uid
|| !*override_identity_uid
) && from
) {
3630 const gchar
*name
= NULL
, *address
= NULL
;
3632 if (camel_address_length (CAMEL_ADDRESS (from
)) == 1 &&
3633 camel_internet_address_get (from
, 0, &name
, &address
)) {
3634 EComposerFromHeader
*header_from
;
3635 const gchar
*filled_name
, *filled_address
;
3637 /* First try whether such alias exists... */
3638 e_composer_header_table_set_identity_uid (table
, identity_uid
, name
, address
);
3640 header_from
= E_COMPOSER_FROM_HEADER (e_composer_header_table_get_header (table
, E_COMPOSER_HEADER_FROM
));
3642 filled_name
= e_composer_from_header_get_name (header_from
);
3643 filled_address
= e_composer_from_header_get_address (header_from
);
3648 if (address
&& !*address
)
3651 if (g_strcmp0 (filled_name
, name
) != 0 ||
3652 g_strcmp0 (filled_address
, address
) != 0) {
3653 /* ... and if not, then reset to the main identity address */
3654 e_composer_header_table_set_identity_uid (table
, identity_uid
, NULL
, NULL
);
3655 e_composer_from_header_set_name (header_from
, name
);
3656 e_composer_from_header_set_address (header_from
, address
);
3657 e_composer_from_header_set_override_visible (header_from
, TRUE
);
3660 e_composer_header_table_set_identity_uid (table
, identity_uid
, NULL
, NULL
);
3663 e_composer_header_table_set_identity_uid (table
, identity_uid
, override_alias_name
, override_alias_address
);
3666 g_free (identity_uid
);
3668 /* Restore the format editing preference */
3669 format
= camel_medium_get_header (
3670 CAMEL_MEDIUM (message
), "X-Evolution-Format");
3672 composer_mode
= camel_medium_get_header (
3673 CAMEL_MEDIUM (message
), "X-Evolution-Composer-Mode");
3675 if (composer_mode
&& *composer_mode
)
3676 is_message_from_draft
= TRUE
;
3678 if (format
!= NULL
) {
3681 while (*format
&& camel_mime_is_lwsp (*format
))
3684 flags
= g_strsplit (format
, ", ", 0);
3685 for (i
= 0; flags
[i
]; i
++) {
3686 if (g_ascii_strcasecmp (flags
[i
], "text/html") == 0 ||
3687 g_ascii_strcasecmp (flags
[i
], "text/plain") == 0) {
3690 html_mode
= composer_mode
&& !g_ascii_strcasecmp (composer_mode
, "text/html");
3691 e_content_editor_set_html_mode (cnt_editor
, html_mode
);
3692 } else if (g_ascii_strcasecmp (flags
[i
], "pgp-sign") == 0) {
3693 action
= GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
));
3694 gtk_toggle_action_set_active (action
, TRUE
);
3695 } else if (g_ascii_strcasecmp (flags
[i
], "pgp-encrypt") == 0) {
3696 action
= GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT
));
3697 gtk_toggle_action_set_active (action
, TRUE
);
3698 } else if (g_ascii_strcasecmp (flags
[i
], "smime-sign") == 0) {
3699 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
));
3700 gtk_toggle_action_set_active (action
, TRUE
);
3701 } else if (g_ascii_strcasecmp (flags
[i
], "smime-encrypt") == 0) {
3702 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT
));
3703 gtk_toggle_action_set_active (action
, TRUE
);
3709 if (is_message_from_draft
|| (
3710 camel_medium_get_header (CAMEL_MEDIUM (message
), "X-Evolution-Identity") &&
3711 camel_medium_get_header (CAMEL_MEDIUM (message
), "X-Evolution-Transport"))) {
3712 const gchar
*reply_to
;
3714 reply_to
= camel_medium_get_header (CAMEL_MEDIUM (message
), "Reply-To");
3717 e_composer_header_table_set_reply_to (table
, reply_to
);
3720 /* Remove any other X-Evolution-* headers that may have been set */
3721 camel_name_value_array_free (mail_tool_remove_xevolution_headers (message
));
3723 /* Check for receipt request */
3724 if (camel_medium_get_header (
3725 CAMEL_MEDIUM (message
), "Disposition-Notification-To")) {
3726 action
= GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT
));
3727 gtk_toggle_action_set_active (action
, TRUE
);
3730 /* Check for mail priority */
3731 if (camel_medium_get_header (CAMEL_MEDIUM (message
), "X-Priority")) {
3732 action
= GTK_TOGGLE_ACTION (ACTION (PRIORITIZE_MESSAGE
));
3733 gtk_toggle_action_set_active (action
, TRUE
);
3736 /* set extra headers */
3737 headers
= camel_medium_get_headers (CAMEL_MEDIUM (message
));
3738 jjlen
= camel_name_value_array_get_length (headers
);
3739 for (jj
= 0; jj
< jjlen
; jj
++) {
3740 const gchar
*header_name
= NULL
, *header_value
= NULL
;
3742 if (!camel_name_value_array_get (headers
, jj
, &header_name
, &header_value
) || !header_name
)
3745 if (g_ascii_strcasecmp (header_name
, "References") == 0 ||
3746 g_ascii_strcasecmp (header_name
, "In-Reply-To") == 0) {
3748 composer
->priv
->extra_hdr_names
,
3749 g_strdup (header_name
));
3751 composer
->priv
->extra_hdr_values
,
3752 camel_header_unfold (header_value
));
3756 /* Restore the attachments and body text */
3757 content
= camel_medium_get_content (CAMEL_MEDIUM (message
));
3758 if (CAMEL_IS_MULTIPART (content
)) {
3759 CamelMimePart
*mime_part
;
3760 CamelMultipart
*multipart
;
3762 multipart
= CAMEL_MULTIPART (content
);
3763 mime_part
= CAMEL_MIME_PART (message
);
3764 content_type
= camel_mime_part_get_content_type (mime_part
);
3766 if (CAMEL_IS_MULTIPART_SIGNED (content
)) {
3767 /* Handle the signed content and configure the
3768 * composer to sign outgoing messages. */
3769 handle_multipart_signed (
3770 composer
, multipart
, keep_signature
, cancellable
, 0);
3772 } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content
)) {
3773 /* Decrypt the encrypted content and configure the
3774 * composer to encrypt outgoing messages. */
3775 handle_multipart_encrypted (
3776 composer
, mime_part
, keep_signature
, cancellable
, 0);
3778 } else if (camel_content_type_is (
3779 content_type
, "multipart", "alternative")) {
3780 /* This contains the text/plain and text/html
3781 * versions of the message body. */
3782 handle_multipart_alternative (
3783 composer
, multipart
, keep_signature
, cancellable
, 0);
3786 /* There must be attachments... */
3788 composer
, multipart
, keep_signature
, cancellable
, 0);
3791 CamelMimePart
*mime_part
;
3792 gboolean is_html
= FALSE
;
3796 mime_part
= CAMEL_MIME_PART (message
);
3797 content_type
= camel_mime_part_get_content_type (mime_part
);
3798 is_html
= camel_content_type_is (content_type
, "text", "html");
3800 if (content_type
!= NULL
&& (
3801 camel_content_type_is (
3802 content_type
, "application", "x-pkcs7-mime") ||
3803 camel_content_type_is (
3804 content_type
, "application", "pkcs7-mime"))) {
3806 gtk_toggle_action_set_active (
3808 ACTION (SMIME_ENCRYPT
)), TRUE
);
3811 /* If we are opening message from Drafts */
3812 if (is_message_from_draft
) {
3813 /* Extract the body */
3814 CamelDataWrapper
*dw
;
3816 dw
= camel_medium_get_content ((CamelMedium
*) mime_part
);
3818 CamelStream
*mem
= camel_stream_mem_new ();
3821 camel_data_wrapper_decode_to_stream_sync (dw
, mem
, cancellable
, NULL
);
3822 camel_stream_close (mem
, cancellable
, NULL
);
3824 bytes
= camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem
));
3825 if (bytes
&& bytes
->len
)
3826 html
= g_strndup ((const gchar
*) bytes
->data
, bytes
->len
);
3828 g_object_unref (mem
);
3832 html
= emcu_part_to_html (
3833 composer
, CAMEL_MIME_PART (message
),
3834 &length
, keep_signature
, cancellable
);
3836 e_msg_composer_set_pending_body (composer
, html
, length
, is_html
);
3839 priv
->set_signature_from_message
= TRUE
;
3841 /* We wait until now to set the body text because we need to
3842 * ensure that the attachment bar has all the attachments before
3843 * we request them. */
3844 e_msg_composer_flush_pending_body (composer
);
3846 set_signature_gui (composer
);
3850 * e_msg_composer_setup_redirect:
3851 * @composer: an #EMsgComposer
3852 * @message: The message to use as the source
3853 * @identity_uid: (nullable): an identity UID to use, if any
3854 * @alias_name: (nullable): an alias name to use together with the identity_uid, or %NULL
3855 * @alias_address: (nullable): an alias address to use together with the identity_uid, or %NULL
3856 * @cancellable: an optional #GCancellable
3858 * Sets up the message @composer as a redirect of the @message.
3863 e_msg_composer_setup_redirect (EMsgComposer
*composer
,
3864 CamelMimeMessage
*message
,
3865 const gchar
*identity_uid
,
3866 const gchar
*alias_name
,
3867 const gchar
*alias_address
,
3868 GCancellable
*cancellable
)
3870 EComposerHeaderTable
*table
;
3871 EHTMLEditor
*editor
;
3872 EContentEditor
*cnt_editor
;
3873 const gchar
*subject
;
3875 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
3876 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
3878 e_msg_composer_setup_with_message (composer
, message
, TRUE
, identity_uid
, alias_name
, alias_address
, cancellable
);
3880 table
= e_msg_composer_get_header_table (composer
);
3881 subject
= camel_mime_message_get_subject (message
);
3883 composer
->priv
->redirect
= message
;
3884 g_object_ref (message
);
3886 e_composer_header_table_set_subject (table
, subject
);
3888 editor
= e_msg_composer_get_editor (composer
);
3889 cnt_editor
= e_html_editor_get_content_editor (editor
);
3890 e_content_editor_set_editable (cnt_editor
, FALSE
);
3894 * e_msg_composer_ref_session:
3895 * @composer: an #EMsgComposer
3897 * Returns the mail module's global #CamelSession instance. Calling
3898 * this function will load the mail module if it isn't already loaded.
3900 * The returned #CamelSession is referenced for thread-safety and must
3901 * be unreferenced with g_object_unref() when finished with it.
3903 * Returns: the mail module's #CamelSession
3906 e_msg_composer_ref_session (EMsgComposer
*composer
)
3909 EShellBackend
*shell_backend
;
3910 CamelSession
*session
= NULL
;
3912 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
3914 shell
= e_msg_composer_get_shell (composer
);
3915 shell_backend
= e_shell_get_backend_by_name (shell
, "mail");
3917 g_object_get (shell_backend
, "session", &session
, NULL
);
3918 g_return_val_if_fail (CAMEL_IS_SESSION (session
), NULL
);
3924 * e_msg_composer_get_shell:
3925 * @composer: an #EMsgComposer
3927 * Returns the #EShell that was passed to e_msg_composer_new().
3929 * Returns: the #EShell
3932 e_msg_composer_get_shell (EMsgComposer
*composer
)
3934 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
3936 return E_SHELL (composer
->priv
->shell
);
3940 msg_composer_send_cb (EMsgComposer
*composer
,
3941 GAsyncResult
*result
,
3942 AsyncContext
*context
)
3944 CamelMimeMessage
*message
;
3945 EAlertSink
*alert_sink
;
3946 EHTMLEditor
*editor
;
3947 EContentEditor
*cnt_editor
;
3948 GError
*error
= NULL
;
3950 alert_sink
= e_activity_get_alert_sink (context
->activity
);
3952 message
= e_msg_composer_get_message_finish (composer
, result
, &error
);
3954 if (e_activity_handle_cancellation (context
->activity
, error
)) {
3955 g_warn_if_fail (message
== NULL
);
3956 async_context_free (context
);
3957 g_error_free (error
);
3959 gtk_window_present (GTK_WINDOW (composer
));
3963 if (error
!= NULL
) {
3964 g_warn_if_fail (message
== NULL
);
3967 "mail-composer:no-build-message",
3968 error
->message
, NULL
);
3969 async_context_free (context
);
3970 g_error_free (error
);
3972 gtk_window_present (GTK_WINDOW (composer
));
3976 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
3978 /* The callback can set editor 'changed' if anything failed. */
3979 editor
= e_msg_composer_get_editor (composer
);
3980 cnt_editor
= e_html_editor_get_content_editor (editor
);
3981 e_content_editor_set_changed (cnt_editor
, TRUE
);
3983 composer
->priv
->is_sending_message
= TRUE
;
3986 composer
, signals
[SEND
], 0,
3987 message
, context
->activity
);
3989 composer
->priv
->is_sending_message
= FALSE
;
3991 g_object_unref (message
);
3993 async_context_free (context
);
3997 * e_msg_composer_send:
3998 * @composer: an #EMsgComposer
4000 * Send the message in @composer.
4003 e_msg_composer_send (EMsgComposer
*composer
)
4005 EHTMLEditor
*editor
;
4006 AsyncContext
*context
;
4007 GCancellable
*cancellable
;
4008 gboolean proceed_with_send
= TRUE
;
4010 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4012 /* This gives the user a chance to abort the send. */
4013 g_signal_emit (composer
, signals
[PRESEND
], 0, &proceed_with_send
);
4015 if (!proceed_with_send
) {
4016 gtk_window_present (GTK_WINDOW (composer
));
4020 editor
= e_msg_composer_get_editor (composer
);
4022 context
= g_slice_new0 (AsyncContext
);
4023 context
->activity
= e_html_editor_new_activity (editor
);
4025 cancellable
= e_activity_get_cancellable (context
->activity
);
4027 e_msg_composer_get_message (
4028 composer
, G_PRIORITY_DEFAULT
, cancellable
,
4029 (GAsyncReadyCallback
) msg_composer_send_cb
,
4034 msg_composer_save_to_drafts_done_cb (gpointer user_data
,
4035 GObject
*gone_object
)
4037 EMsgComposer
*composer
= user_data
;
4038 EHTMLEditor
*editor
;
4039 EContentEditor
*cnt_editor
;
4041 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4043 editor
= e_msg_composer_get_editor (composer
);
4044 cnt_editor
= e_html_editor_get_content_editor (editor
);
4046 if (e_msg_composer_is_exiting (composer
) &&
4047 !e_content_editor_get_changed (cnt_editor
)) {
4048 e_composer_emit_before_destroy (composer
);
4049 gtk_widget_destroy (GTK_WIDGET (composer
));
4050 } else if (e_msg_composer_is_exiting (composer
)) {
4051 gtk_widget_set_sensitive (GTK_WIDGET (composer
), TRUE
);
4052 gtk_window_present (GTK_WINDOW (composer
));
4053 composer
->priv
->application_exiting
= FALSE
;
4058 msg_composer_save_to_drafts_cb (EMsgComposer
*composer
,
4059 GAsyncResult
*result
,
4060 AsyncContext
*context
)
4062 CamelMimeMessage
*message
;
4063 EAlertSink
*alert_sink
;
4064 EHTMLEditor
*editor
;
4065 EContentEditor
*cnt_editor
;
4066 GError
*error
= NULL
;
4068 alert_sink
= e_activity_get_alert_sink (context
->activity
);
4070 message
= e_msg_composer_get_message_draft_finish (
4071 composer
, result
, &error
);
4073 if (e_activity_handle_cancellation (context
->activity
, error
)) {
4074 g_warn_if_fail (message
== NULL
);
4075 async_context_free (context
);
4076 g_error_free (error
);
4078 if (e_msg_composer_is_exiting (composer
)) {
4079 gtk_window_present (GTK_WINDOW (composer
));
4080 composer
->priv
->application_exiting
= FALSE
;
4086 if (error
!= NULL
) {
4087 g_warn_if_fail (message
== NULL
);
4090 "mail-composer:no-build-message",
4091 error
->message
, NULL
);
4092 async_context_free (context
);
4093 g_error_free (error
);
4095 if (e_msg_composer_is_exiting (composer
)) {
4096 gtk_window_present (GTK_WINDOW (composer
));
4097 composer
->priv
->application_exiting
= FALSE
;
4103 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
4105 /* The callback can set editor 'changed' if anything failed. */
4106 editor
= e_msg_composer_get_editor (composer
);
4107 cnt_editor
= e_html_editor_get_content_editor (editor
);
4108 e_content_editor_set_changed (cnt_editor
, TRUE
);
4111 composer
, signals
[SAVE_TO_DRAFTS
],
4112 0, message
, context
->activity
);
4114 g_object_unref (message
);
4116 if (e_msg_composer_is_exiting (composer
))
4118 G_OBJECT (context
->activity
),
4119 msg_composer_save_to_drafts_done_cb
, composer
);
4121 async_context_free (context
);
4125 * e_msg_composer_save_to_drafts:
4126 * @composer: an #EMsgComposer
4128 * Save the message in @composer to the selected account's Drafts folder.
4131 e_msg_composer_save_to_drafts (EMsgComposer
*composer
)
4133 EHTMLEditor
*editor
;
4134 AsyncContext
*context
;
4135 GCancellable
*cancellable
;
4137 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4139 editor
= e_msg_composer_get_editor (composer
);
4141 context
= g_slice_new0 (AsyncContext
);
4142 context
->activity
= e_html_editor_new_activity (editor
);
4144 cancellable
= e_activity_get_cancellable (context
->activity
);
4146 e_msg_composer_get_message_draft (
4147 composer
, G_PRIORITY_DEFAULT
, cancellable
,
4148 (GAsyncReadyCallback
) msg_composer_save_to_drafts_cb
,
4153 msg_composer_save_to_outbox_cb (EMsgComposer
*composer
,
4154 GAsyncResult
*result
,
4155 AsyncContext
*context
)
4157 CamelMimeMessage
*message
;
4158 EAlertSink
*alert_sink
;
4159 EHTMLEditor
*editor
;
4160 EContentEditor
*cnt_editor
;
4161 GError
*error
= NULL
;
4163 alert_sink
= e_activity_get_alert_sink (context
->activity
);
4165 message
= e_msg_composer_get_message_finish (composer
, result
, &error
);
4167 if (e_activity_handle_cancellation (context
->activity
, error
)) {
4168 g_warn_if_fail (message
== NULL
);
4169 async_context_free (context
);
4170 g_error_free (error
);
4174 if (error
!= NULL
) {
4175 g_warn_if_fail (message
== NULL
);
4178 "mail-composer:no-build-message",
4179 error
->message
, NULL
);
4180 async_context_free (context
);
4181 g_error_free (error
);
4185 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
4188 composer
, signals
[SAVE_TO_OUTBOX
],
4189 0, message
, context
->activity
);
4191 g_object_unref (message
);
4193 async_context_free (context
);
4195 editor
= e_msg_composer_get_editor (composer
);
4196 cnt_editor
= e_html_editor_get_content_editor (editor
);
4197 e_content_editor_set_changed (cnt_editor
, TRUE
);
4201 * e_msg_composer_save_to_outbox:
4202 * @composer: an #EMsgComposer
4204 * Save the message in @composer to the local Outbox folder.
4207 e_msg_composer_save_to_outbox (EMsgComposer
*composer
)
4209 EHTMLEditor
*editor
;
4210 AsyncContext
*context
;
4211 GCancellable
*cancellable
;
4213 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4215 if (!composer
->priv
->is_sending_message
) {
4216 gboolean proceed_with_save
= TRUE
;
4218 /* This gives the user a chance to abort the save. */
4219 g_signal_emit (composer
, signals
[PRESEND
], 0, &proceed_with_save
);
4221 if (!proceed_with_save
)
4225 editor
= e_msg_composer_get_editor (composer
);
4227 context
= g_slice_new0 (AsyncContext
);
4228 context
->activity
= e_html_editor_new_activity (editor
);
4230 cancellable
= e_activity_get_cancellable (context
->activity
);
4232 e_msg_composer_get_message (
4233 composer
, G_PRIORITY_DEFAULT
, cancellable
,
4234 (GAsyncReadyCallback
) msg_composer_save_to_outbox_cb
,
4239 msg_composer_print_cb (EMsgComposer
*composer
,
4240 GAsyncResult
*result
,
4241 AsyncContext
*context
)
4243 CamelMimeMessage
*message
;
4244 EAlertSink
*alert_sink
;
4245 GError
*error
= NULL
;
4247 alert_sink
= e_activity_get_alert_sink (context
->activity
);
4249 message
= e_msg_composer_get_message_print_finish (
4250 composer
, result
, &error
);
4252 if (e_activity_handle_cancellation (context
->activity
, error
)) {
4253 g_warn_if_fail (message
== NULL
);
4254 async_context_free (context
);
4255 g_error_free (error
);
4259 if (error
!= NULL
) {
4260 g_warn_if_fail (message
== NULL
);
4261 async_context_free (context
);
4264 "mail-composer:no-build-message",
4265 error
->message
, NULL
);
4266 g_error_free (error
);
4270 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
4273 composer
, signals
[PRINT
], 0,
4274 context
->print_action
, message
, context
->activity
);
4276 g_object_unref (message
);
4278 async_context_free (context
);
4282 * e_msg_composer_print:
4283 * @composer: an #EMsgComposer
4284 * @print_action: the print action to start
4286 * Print the message in @composer.
4289 e_msg_composer_print (EMsgComposer
*composer
,
4290 GtkPrintOperationAction print_action
)
4292 EHTMLEditor
*editor
;
4293 AsyncContext
*context
;
4294 GCancellable
*cancellable
;
4296 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4298 editor
= e_msg_composer_get_editor (composer
);
4300 context
= g_slice_new0 (AsyncContext
);
4301 context
->activity
= e_html_editor_new_activity (editor
);
4302 context
->print_action
= print_action
;
4304 cancellable
= e_activity_get_cancellable (context
->activity
);
4306 e_msg_composer_get_message_print (
4307 composer
, G_PRIORITY_DEFAULT
, cancellable
,
4308 (GAsyncReadyCallback
) msg_composer_print_cb
,
4313 add_recipients (GList
*list
,
4314 const gchar
*recips
)
4316 CamelInternetAddress
*cia
;
4317 const gchar
*name
, *addr
;
4320 cia
= camel_internet_address_new ();
4321 num
= camel_address_decode (CAMEL_ADDRESS (cia
), recips
);
4323 for (i
= 0; i
< num
; i
++) {
4324 if (camel_internet_address_get (cia
, i
, &name
, &addr
)) {
4325 EDestination
*dest
= e_destination_new ();
4326 e_destination_set_name (dest
, name
);
4327 e_destination_set_email (dest
, addr
);
4329 list
= g_list_append (list
, dest
);
4333 g_object_unref (cia
);
4339 list_contains_addr (const GList
*list
,
4342 g_return_val_if_fail (dest
!= NULL
, FALSE
);
4344 while (list
!= NULL
) {
4345 if (e_destination_equal (dest
, list
->data
))
4355 merge_cc_bcc (EDestination
**addrv
,
4363 for (ii
= 0; addrv
&& addrv
[ii
]; ii
++) {
4364 if (!list_contains_addr (to
, addrv
[ii
]) &&
4365 !list_contains_addr (cc
, addrv
[ii
]) &&
4366 !list_contains_addr (bcc
, addrv
[ii
])) {
4367 *merge_into
= g_list_append (
4368 *merge_into
, g_object_ref (addrv
[ii
]));
4374 merge_always_cc_and_bcc (EComposerHeaderTable
*table
,
4379 EDestination
**addrv
;
4381 g_return_if_fail (table
!= NULL
);
4382 g_return_if_fail (cc
!= NULL
);
4383 g_return_if_fail (bcc
!= NULL
);
4385 addrv
= e_composer_header_table_get_destinations_cc (table
);
4386 merge_cc_bcc (addrv
, cc
, to
, *cc
, *bcc
);
4387 e_destination_freev (addrv
);
4389 addrv
= e_composer_header_table_get_destinations_bcc (table
);
4390 merge_cc_bcc (addrv
, bcc
, to
, *cc
, *bcc
);
4391 e_destination_freev (addrv
);
4394 static const gchar
*blacklist
[] = { ".", "etc", ".." };
4397 file_is_blacklisted (const gchar
*argument
)
4400 gboolean blacklisted
= FALSE
;
4401 guint ii
, jj
, n_parts
;
4405 /* The "attach" argument may be a URI or local path. Normalize
4406 * it to a local path if we can. We only blacklist local files. */
4407 file
= g_file_new_for_commandline_arg (argument
);
4408 filename
= g_file_get_path (file
);
4409 g_object_unref (file
);
4411 if (filename
== NULL
)
4414 parts
= g_strsplit (filename
, G_DIR_SEPARATOR_S
, -1);
4415 n_parts
= g_strv_length (parts
);
4417 for (ii
= 0; ii
< G_N_ELEMENTS (blacklist
); ii
++) {
4418 for (jj
= 0; jj
< n_parts
; jj
++) {
4419 if (g_str_has_prefix (parts
[jj
], blacklist
[ii
])) {
4429 /* Don't blacklist files in trusted base directories. */
4430 if (g_str_has_prefix (filename
, g_get_user_data_dir ()))
4431 blacklisted
= FALSE
;
4432 if (g_str_has_prefix (filename
, g_get_user_cache_dir ()))
4433 blacklisted
= FALSE
;
4434 if (g_str_has_prefix (filename
, g_get_user_config_dir ()))
4435 blacklisted
= FALSE
;
4437 /* Apparently KDE still uses ~/.kde heavily, and some
4438 * distributions use ~/.kde4 to distinguish KDE4 data
4439 * from KDE3 data. Trust these directories as well. */
4441 base_dir
= g_build_filename (g_get_home_dir (), ".kde", NULL
);
4442 if (g_str_has_prefix (filename
, base_dir
))
4443 blacklisted
= FALSE
;
4446 base_dir
= g_build_filename (g_get_home_dir (), ".kde4", NULL
);
4447 if (g_str_has_prefix (filename
, base_dir
))
4448 blacklisted
= FALSE
;
4459 handle_mailto (EMsgComposer
*composer
,
4460 const gchar
*mailto
)
4462 EAttachmentView
*view
;
4463 EAttachmentStore
*store
;
4464 EComposerHeaderTable
*table
;
4465 GList
*to
= NULL
, *cc
= NULL
, *bcc
= NULL
;
4466 EDestination
**tov
, **ccv
, **bccv
;
4467 gchar
*subject
= NULL
, *body
= NULL
;
4468 gchar
*header
, *content
, *buf
;
4469 gsize nread
, nwritten
;
4473 table
= e_msg_composer_get_header_table (composer
);
4474 view
= e_msg_composer_get_attachment_view (composer
);
4475 store
= e_attachment_view_get_store (view
);
4477 buf
= g_strdup (mailto
);
4479 /* Parse recipients (everything after ':' and up to three leading forward slashes until '?' or eos). */
4482 while (*p
== '/' && p
- buf
< 10)
4485 len
= strcspn (p
, "?");
4487 content
= g_strndup (p
, len
);
4488 camel_url_decode (content
);
4489 to
= add_recipients (to
, content
);
4498 len
= strcspn (p
, "=&");
4500 /* If it's malformed, give up. */
4504 header
= (gchar
*) p
;
4508 clen
= strcspn (p
, "&");
4510 content
= g_strndup (p
, clen
);
4512 if (!g_ascii_strcasecmp (header
, "to")) {
4513 camel_url_decode (content
);
4514 to
= add_recipients (to
, content
);
4515 } else if (!g_ascii_strcasecmp (header
, "cc")) {
4516 camel_url_decode (content
);
4517 cc
= add_recipients (cc
, content
);
4518 } else if (!g_ascii_strcasecmp (header
, "bcc")) {
4519 camel_url_decode (content
);
4520 bcc
= add_recipients (bcc
, content
);
4521 } else if (!g_ascii_strcasecmp (header
, "subject")) {
4523 camel_url_decode (content
);
4524 if (g_utf8_validate (content
, -1, NULL
)) {
4528 subject
= g_locale_to_utf8 (
4529 content
, clen
, &nread
,
4532 subject
= g_realloc (subject
, nwritten
+ 1);
4533 subject
[nwritten
] = '\0';
4536 } else if (!g_ascii_strcasecmp (header
, "body")) {
4538 camel_url_decode (content
);
4539 if (g_utf8_validate (content
, -1, NULL
)) {
4543 body
= g_locale_to_utf8 (
4544 content
, clen
, &nread
,
4547 body
= g_realloc (body
, nwritten
+ 1);
4548 body
[nwritten
] = '\0';
4551 } else if (!g_ascii_strcasecmp (header
, "attach") ||
4552 !g_ascii_strcasecmp (header
, "attachment")) {
4553 EAttachment
*attachment
;
4555 camel_url_decode (content
);
4556 if (file_is_blacklisted (content
))
4558 E_ALERT_SINK (e_msg_composer_get_editor (composer
)),
4559 "mail:blacklisted-file",
4561 if (g_ascii_strncasecmp (content
, "file:", 5) == 0)
4562 attachment
= e_attachment_new_for_uri (content
);
4564 attachment
= e_attachment_new_for_path (content
);
4565 e_attachment_store_add_attachment (store
, attachment
);
4566 e_attachment_load_async (
4567 attachment
, (GAsyncReadyCallback
)
4568 e_attachment_load_handle_error
, composer
);
4569 g_object_unref (attachment
);
4570 } else if (!g_ascii_strcasecmp (header
, "from")) {
4572 } else if (!g_ascii_strcasecmp (header
, "reply-to")) {
4575 /* add an arbitrary header? */
4576 camel_url_decode (content
);
4577 e_msg_composer_add_header (composer
, header
, content
);
4585 if (!g_ascii_strncasecmp (p
, "amp;", 4))
4593 merge_always_cc_and_bcc (table
, to
, &cc
, &bcc
);
4595 tov
= destination_list_to_vector (to
);
4596 ccv
= destination_list_to_vector (cc
);
4597 bccv
= destination_list_to_vector (bcc
);
4603 e_composer_header_table_set_destinations_to (table
, tov
);
4604 e_composer_header_table_set_destinations_cc (table
, ccv
);
4605 e_composer_header_table_set_destinations_bcc (table
, bccv
);
4607 e_destination_freev (tov
);
4608 e_destination_freev (ccv
);
4609 e_destination_freev (bccv
);
4611 e_composer_header_table_set_subject (table
, subject
);
4615 GSettings
*settings
;
4619 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
4621 if (g_settings_get_boolean (settings
, "composer-magic-links")) {
4622 flags
|= CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS
| CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES
;
4625 if (g_settings_get_boolean (settings
, "composer-mailto-body-in-pre"))
4626 flags
|= CAMEL_MIME_FILTER_TOHTML_PRE
;
4628 flags
|= CAMEL_MIME_FILTER_TOHTML_CONVERT_NL
| CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES
;
4630 g_clear_object (&settings
);
4632 html_body
= camel_text_to_html (body
, flags
, 0);
4633 set_editor_text (composer
, html_body
, TRUE
, TRUE
);
4639 * e_msg_composer_setup_from_url:
4640 * @composer: an #EMsgComposer
4641 * @url: a mailto URL
4643 * Sets up the message @composer content as defined by the provided URL.
4648 e_msg_composer_setup_from_url (EMsgComposer
*composer
,
4651 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4652 g_return_if_fail (g_ascii_strncasecmp (url
, "mailto:", 7) == 0);
4654 handle_mailto (composer
, url
);
4658 * e_msg_composer_set_body_text:
4659 * @composer: a composer object
4660 * @text: the HTML text to initialize the editor with
4661 * @update_signature: whether update signature in the text after setting it;
4662 * Might be usually called with TRUE.
4664 * Loads the given HTML text into the editor.
4667 e_msg_composer_set_body_text (EMsgComposer
*composer
,
4669 gboolean update_signature
)
4671 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4672 g_return_if_fail (text
!= NULL
);
4674 /* Every usage of e_msg_composer_set_body_text is called with HTML text */
4675 set_editor_text (composer
, text
, TRUE
, update_signature
);
4679 * e_msg_composer_set_body:
4680 * @composer: a composer object
4681 * @body: the data to initialize the composer with
4682 * @mime_type: the MIME type of data
4684 * Loads the given data into the composer as the message body.
4687 e_msg_composer_set_body (EMsgComposer
*composer
,
4689 const gchar
*mime_type
)
4691 EMsgComposerPrivate
*priv
= composer
->priv
;
4692 EComposerHeaderTable
*table
;
4693 EHTMLEditor
*editor
;
4694 EContentEditor
*cnt_editor
;
4696 gchar
*identity_uid
;
4697 const gchar
*content
;
4699 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4701 editor
= e_msg_composer_get_editor (composer
);
4702 cnt_editor
= e_html_editor_get_content_editor (editor
);
4703 table
= e_msg_composer_get_header_table (composer
);
4705 /* Disable signature */
4706 priv
->disable_signature
= TRUE
;
4708 identity_uid
= e_composer_header_table_dup_identity_uid (table
, NULL
, NULL
);
4709 source
= e_composer_header_table_ref_source (table
, identity_uid
);
4711 content
= _("The composer contains a non-text message body, which cannot be edited.");
4712 set_editor_text (composer
, content
, TRUE
, FALSE
);
4714 e_content_editor_set_html_mode (cnt_editor
, FALSE
);
4715 e_content_editor_set_editable (cnt_editor
, FALSE
);
4717 g_free (priv
->mime_body
);
4718 priv
->mime_body
= g_strdup (body
);
4719 g_free (priv
->mime_type
);
4720 priv
->mime_type
= g_strdup (mime_type
);
4722 if (g_ascii_strncasecmp (priv
->mime_type
, "text/calendar", 13) == 0) {
4723 ESourceMailComposition
*extension
;
4724 const gchar
*extension_name
;
4726 extension_name
= E_SOURCE_EXTENSION_MAIL_COMPOSITION
;
4727 extension
= e_source_get_extension (source
, extension_name
);
4729 if (!e_source_mail_composition_get_sign_imip (extension
)) {
4730 GtkToggleAction
*action
;
4732 action
= GTK_TOGGLE_ACTION (ACTION (PGP_SIGN
));
4733 gtk_toggle_action_set_active (action
, FALSE
);
4735 action
= GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN
));
4736 gtk_toggle_action_set_active (action
, FALSE
);
4740 g_object_unref (source
);
4741 g_free (identity_uid
);
4745 * e_msg_composer_add_header:
4746 * @composer: an #EMsgComposer
4747 * @name: the header's name
4748 * @value: the header's value
4750 * Adds a new custom header created from @name and @value. The header
4751 * is not shown in the user interface but will be added to the resulting
4752 * MIME message when sending or saving.
4755 e_msg_composer_add_header (EMsgComposer
*composer
,
4759 EMsgComposerPrivate
*priv
;
4761 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4762 g_return_if_fail (name
!= NULL
);
4763 g_return_if_fail (value
!= NULL
);
4765 priv
= composer
->priv
;
4767 g_ptr_array_add (priv
->extra_hdr_names
, g_strdup (name
));
4768 g_ptr_array_add (priv
->extra_hdr_values
, g_strdup (value
));
4772 * e_msg_composer_set_header:
4773 * @composer: an #EMsgComposer
4774 * @name: the header's name
4775 * @value: the header's value
4777 * Replaces all custom headers matching @name that were added with
4778 * e_msg_composer_add_header() or e_msg_composer_set_header(), with
4779 * a new custom header created from @name and @value. The header is
4780 * not shown in the user interface but will be added to the resulting
4781 * MIME message when sending or saving.
4784 e_msg_composer_set_header (EMsgComposer
*composer
,
4788 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4789 g_return_if_fail (name
!= NULL
);
4790 g_return_if_fail (value
!= NULL
);
4792 e_msg_composer_remove_header (composer
, name
);
4793 e_msg_composer_add_header (composer
, name
, value
);
4797 * e_msg_composer_remove_header:
4798 * @composer: an #EMsgComposer
4799 * @name: the header's name
4801 * Removes all custom headers matching @name that were added with
4802 * e_msg_composer_add_header() or e_msg_composer_set_header().
4805 e_msg_composer_remove_header (EMsgComposer
*composer
,
4808 EMsgComposerPrivate
*priv
;
4811 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4812 g_return_if_fail (name
!= NULL
);
4814 priv
= composer
->priv
;
4816 for (ii
= 0; ii
< priv
->extra_hdr_names
->len
; ii
++) {
4817 if (g_strcmp0 (priv
->extra_hdr_names
->pdata
[ii
], name
) == 0) {
4818 g_free (priv
->extra_hdr_names
->pdata
[ii
]);
4819 g_free (priv
->extra_hdr_values
->pdata
[ii
]);
4820 g_ptr_array_remove_index (priv
->extra_hdr_names
, ii
);
4821 g_ptr_array_remove_index (priv
->extra_hdr_values
, ii
);
4827 * e_msg_composer_get_header:
4828 * @composer: an #EMsgComposer
4829 * @name: the header's name
4830 * @index: index of the header, 0-based
4832 * Returns header value of the header named @name previously added
4833 * by e_msg_composer_add_header() or set by e_msg_composer_set_header().
4834 * The @index is which header index to return. Returns %NULL on error
4835 * or when the given index of the header couldn't be found.
4837 * Returns: stored header value or NULL, if couldn't be found.
4842 e_msg_composer_get_header (EMsgComposer
*composer
,
4846 EMsgComposerPrivate
*priv
;
4849 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
4850 g_return_val_if_fail (name
!= NULL
, NULL
);
4852 priv
= composer
->priv
;
4854 for (ii
= 0; ii
< priv
->extra_hdr_names
->len
; ii
++) {
4855 if (g_strcmp0 (priv
->extra_hdr_names
->pdata
[ii
], name
) == 0) {
4857 return priv
->extra_hdr_values
->pdata
[ii
];
4867 * e_msg_composer_set_draft_headers:
4868 * @composer: an #EMsgComposer
4869 * @folder_uri: folder URI of the last saved draft
4870 * @message_uid: message UID of the last saved draft
4872 * Add special X-Evolution-Draft headers to remember the most recently
4873 * saved draft message, even across Evolution sessions. These headers
4874 * can be used to mark the draft message for deletion after saving a
4875 * newer draft or sending the composed message.
4878 e_msg_composer_set_draft_headers (EMsgComposer
*composer
,
4879 const gchar
*folder_uri
,
4880 const gchar
*message_uid
)
4882 const gchar
*header_name
;
4884 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4885 g_return_if_fail (folder_uri
!= NULL
);
4886 g_return_if_fail (message_uid
!= NULL
);
4888 header_name
= "X-Evolution-Draft-Folder";
4889 e_msg_composer_set_header (composer
, header_name
, folder_uri
);
4891 header_name
= "X-Evolution-Draft-Message";
4892 e_msg_composer_set_header (composer
, header_name
, message_uid
);
4896 * e_msg_composer_set_source_headers:
4897 * @composer: an #EMsgComposer
4898 * @folder_uri: folder URI of the source message
4899 * @message_uid: message UID of the source message
4900 * @flags: flags to set on the source message after sending
4902 * Add special X-Evolution-Source headers to remember the message being
4903 * forwarded or replied to, even across Evolution sessions. These headers
4904 * can be used to set appropriate flags on the source message after sending
4905 * the composed message.
4908 e_msg_composer_set_source_headers (EMsgComposer
*composer
,
4909 const gchar
*folder_uri
,
4910 const gchar
*message_uid
,
4911 CamelMessageFlags flags
)
4914 const gchar
*header_name
;
4916 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4917 g_return_if_fail (folder_uri
!= NULL
);
4918 g_return_if_fail (message_uid
!= NULL
);
4920 buffer
= g_string_sized_new (32);
4922 if (flags
& CAMEL_MESSAGE_ANSWERED
)
4923 g_string_append (buffer
, "ANSWERED ");
4924 if (flags
& CAMEL_MESSAGE_ANSWERED_ALL
)
4925 g_string_append (buffer
, "ANSWERED_ALL ");
4926 if (flags
& CAMEL_MESSAGE_FORWARDED
)
4927 g_string_append (buffer
, "FORWARDED ");
4928 if (flags
& CAMEL_MESSAGE_SEEN
)
4929 g_string_append (buffer
, "SEEN ");
4931 header_name
= "X-Evolution-Source-Folder";
4932 e_msg_composer_set_header (composer
, header_name
, folder_uri
);
4934 header_name
= "X-Evolution-Source-Message";
4935 e_msg_composer_set_header (composer
, header_name
, message_uid
);
4937 header_name
= "X-Evolution-Source-Flags";
4938 e_msg_composer_set_header (composer
, header_name
, buffer
->str
);
4940 g_string_free (buffer
, TRUE
);
4944 * e_msg_composer_attach:
4945 * @composer: a composer object
4946 * @mime_part: the #CamelMimePart to attach
4948 * Attaches @attachment to the message being composed in the composer.
4951 e_msg_composer_attach (EMsgComposer
*composer
,
4952 CamelMimePart
*mime_part
)
4954 EAttachmentView
*view
;
4955 EAttachmentStore
*store
;
4956 EAttachment
*attachment
;
4958 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4959 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part
));
4961 view
= e_msg_composer_get_attachment_view (composer
);
4962 store
= e_attachment_view_get_store (view
);
4964 attachment
= e_attachment_new ();
4965 e_attachment_set_mime_part (attachment
, mime_part
);
4966 e_attachment_store_add_attachment (store
, attachment
);
4967 e_attachment_load_async (
4968 attachment
, (GAsyncReadyCallback
)
4969 e_attachment_load_handle_error
, composer
);
4970 g_object_unref (attachment
);
4974 composer_get_message_ready (EMsgComposer
*composer
,
4975 GAsyncResult
*result
,
4976 GSimpleAsyncResult
*simple
)
4978 CamelMimeMessage
*message
;
4979 GError
*error
= NULL
;
4981 message
= composer_build_message_finish (composer
, result
, &error
);
4983 if (message
!= NULL
)
4984 g_simple_async_result_set_op_res_gpointer (
4985 simple
, message
, (GDestroyNotify
) g_object_unref
);
4987 if (error
!= NULL
) {
4988 g_warn_if_fail (message
== NULL
);
4989 g_simple_async_result_take_error (simple
, error
);
4992 g_simple_async_result_complete (simple
);
4994 g_object_unref (simple
);
4998 * e_msg_composer_get_message:
4999 * @composer: an #EMsgComposer
5001 * Retrieve the message edited by the user as a #CamelMimeMessage. The
5002 * #CamelMimeMessage object is created on the fly; subsequent calls to this
5003 * function will always create new objects from scratch.
5006 e_msg_composer_get_message (EMsgComposer
*composer
,
5008 GCancellable
*cancellable
,
5009 GAsyncReadyCallback callback
,
5012 GSimpleAsyncResult
*simple
;
5014 ComposerFlags flags
= 0;
5015 EHTMLEditor
*editor
;
5016 EContentEditor
*cnt_editor
;
5018 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5020 editor
= e_msg_composer_get_editor (composer
);
5021 cnt_editor
= e_html_editor_get_content_editor (editor
);
5023 simple
= g_simple_async_result_new (
5024 G_OBJECT (composer
), callback
,
5025 user_data
, e_msg_composer_get_message
);
5027 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
5029 if (e_content_editor_get_html_mode (cnt_editor
))
5030 flags
|= COMPOSER_FLAG_HTML_CONTENT
;
5032 action
= ACTION (PRIORITIZE_MESSAGE
);
5033 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5034 flags
|= COMPOSER_FLAG_PRIORITIZE_MESSAGE
;
5036 action
= ACTION (REQUEST_READ_RECEIPT
);
5037 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5038 flags
|= COMPOSER_FLAG_REQUEST_READ_RECEIPT
;
5040 action
= ACTION (PGP_SIGN
);
5041 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5042 flags
|= COMPOSER_FLAG_PGP_SIGN
;
5044 action
= ACTION (PGP_ENCRYPT
);
5045 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5046 flags
|= COMPOSER_FLAG_PGP_ENCRYPT
;
5049 action
= ACTION (SMIME_SIGN
);
5050 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5051 flags
|= COMPOSER_FLAG_SMIME_SIGN
;
5053 action
= ACTION (SMIME_ENCRYPT
);
5054 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5055 flags
|= COMPOSER_FLAG_SMIME_ENCRYPT
;
5058 composer_build_message (
5059 composer
, flags
, io_priority
,
5060 cancellable
, (GAsyncReadyCallback
)
5061 composer_get_message_ready
, simple
);
5065 e_msg_composer_get_message_finish (EMsgComposer
*composer
,
5066 GAsyncResult
*result
,
5069 GSimpleAsyncResult
*simple
;
5070 CamelMimeMessage
*message
;
5072 g_return_val_if_fail (
5073 g_simple_async_result_is_valid (
5074 result
, G_OBJECT (composer
),
5075 e_msg_composer_get_message
), NULL
);
5077 simple
= G_SIMPLE_ASYNC_RESULT (result
);
5078 message
= g_simple_async_result_get_op_res_gpointer (simple
);
5080 if (g_simple_async_result_propagate_error (simple
, error
))
5083 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), NULL
);
5085 return g_object_ref (message
);
5089 e_msg_composer_get_message_print (EMsgComposer
*composer
,
5091 GCancellable
*cancellable
,
5092 GAsyncReadyCallback callback
,
5095 GSimpleAsyncResult
*simple
;
5096 ComposerFlags flags
= 0;
5098 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5100 simple
= g_simple_async_result_new (
5101 G_OBJECT (composer
), callback
,
5102 user_data
, e_msg_composer_get_message_print
);
5104 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
5106 flags
|= COMPOSER_FLAG_HTML_CONTENT
;
5107 flags
|= COMPOSER_FLAG_SAVE_OBJECT_DATA
;
5109 composer_build_message (
5110 composer
, flags
, io_priority
,
5111 cancellable
, (GAsyncReadyCallback
)
5112 composer_get_message_ready
, simple
);
5116 e_msg_composer_get_message_print_finish (EMsgComposer
*composer
,
5117 GAsyncResult
*result
,
5120 GSimpleAsyncResult
*simple
;
5121 CamelMimeMessage
*message
;
5123 g_return_val_if_fail (
5124 g_simple_async_result_is_valid (
5125 result
, G_OBJECT (composer
),
5126 e_msg_composer_get_message_print
), NULL
);
5128 simple
= G_SIMPLE_ASYNC_RESULT (result
);
5129 message
= g_simple_async_result_get_op_res_gpointer (simple
);
5131 if (g_simple_async_result_propagate_error (simple
, error
))
5134 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), NULL
);
5136 return g_object_ref (message
);
5140 e_msg_composer_get_message_draft (EMsgComposer
*composer
,
5142 GCancellable
*cancellable
,
5143 GAsyncReadyCallback callback
,
5146 EHTMLEditor
*editor
;
5147 EContentEditor
*cnt_editor
;
5148 GSimpleAsyncResult
*simple
;
5149 ComposerFlags flags
= COMPOSER_FLAG_SAVE_DRAFT
;
5152 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5154 simple
= g_simple_async_result_new (
5155 G_OBJECT (composer
), callback
,
5156 user_data
, e_msg_composer_get_message_draft
);
5158 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
5160 editor
= e_msg_composer_get_editor (composer
);
5161 cnt_editor
= e_html_editor_get_content_editor (editor
);
5162 /* We need to remember composer mode */
5163 if (e_content_editor_get_html_mode (cnt_editor
))
5164 flags
|= COMPOSER_FLAG_HTML_MODE
;
5165 /* We want to save HTML content everytime when we save as draft */
5166 flags
|= COMPOSER_FLAG_SAVE_DRAFT
;
5168 action
= ACTION (PRIORITIZE_MESSAGE
);
5169 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5170 flags
|= COMPOSER_FLAG_PRIORITIZE_MESSAGE
;
5172 action
= ACTION (REQUEST_READ_RECEIPT
);
5173 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5174 flags
|= COMPOSER_FLAG_REQUEST_READ_RECEIPT
;
5176 action
= ACTION (PGP_SIGN
);
5177 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5178 flags
|= COMPOSER_FLAG_PGP_SIGN
;
5180 action
= ACTION (PGP_ENCRYPT
);
5181 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5182 flags
|= COMPOSER_FLAG_PGP_ENCRYPT
;
5185 action
= ACTION (SMIME_SIGN
);
5186 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5187 flags
|= COMPOSER_FLAG_SMIME_SIGN
;
5189 action
= ACTION (SMIME_ENCRYPT
);
5190 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
5191 flags
|= COMPOSER_FLAG_SMIME_ENCRYPT
;
5194 composer_build_message (
5195 composer
, flags
, io_priority
,
5196 cancellable
, (GAsyncReadyCallback
)
5197 composer_get_message_ready
, simple
);
5201 e_msg_composer_get_message_draft_finish (EMsgComposer
*composer
,
5202 GAsyncResult
*result
,
5205 GSimpleAsyncResult
*simple
;
5206 CamelMimeMessage
*message
;
5208 g_return_val_if_fail (
5209 g_simple_async_result_is_valid (
5210 result
, G_OBJECT (composer
),
5211 e_msg_composer_get_message_draft
), NULL
);
5213 simple
= G_SIMPLE_ASYNC_RESULT (result
);
5214 message
= g_simple_async_result_get_op_res_gpointer (simple
);
5216 if (g_simple_async_result_propagate_error (simple
, error
))
5219 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), NULL
);
5221 return g_object_ref (message
);
5224 CamelInternetAddress
*
5225 e_msg_composer_get_from (EMsgComposer
*composer
)
5227 CamelInternetAddress
*inet_address
= NULL
;
5228 ESourceMailIdentity
*mail_identity
;
5229 EComposerHeaderTable
*table
;
5231 const gchar
*extension_name
;
5232 gchar
*uid
, *alias_name
= NULL
, *alias_address
= NULL
;
5236 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5238 table
= e_msg_composer_get_header_table (composer
);
5240 uid
= e_composer_header_table_dup_identity_uid (table
, &alias_name
, &alias_address
);
5244 source
= e_composer_header_table_ref_source (table
, uid
);
5245 g_return_val_if_fail (source
!= NULL
, NULL
);
5247 extension_name
= E_SOURCE_EXTENSION_MAIL_IDENTITY
;
5248 mail_identity
= e_source_get_extension (source
, extension_name
);
5254 name
= e_source_mail_identity_dup_name (mail_identity
);
5258 name
= e_source_mail_identity_dup_name (mail_identity
);
5260 if (alias_address
) {
5261 address
= alias_address
;
5262 alias_address
= NULL
;
5264 address
= e_source_mail_identity_dup_address (mail_identity
);
5267 g_object_unref (source
);
5269 if (address
!= NULL
) {
5270 inet_address
= camel_internet_address_new ();
5271 camel_internet_address_add (inet_address
, name
, address
);
5277 g_free (alias_name
);
5278 g_free (alias_address
);
5280 return inet_address
;
5283 CamelInternetAddress
*
5284 e_msg_composer_get_reply_to (EMsgComposer
*composer
)
5286 CamelInternetAddress
*address
;
5287 EComposerHeaderTable
*table
;
5288 const gchar
*reply_to
;
5290 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5292 table
= e_msg_composer_get_header_table (composer
);
5294 reply_to
= e_composer_header_table_get_reply_to (table
);
5295 if (reply_to
== NULL
|| *reply_to
== '\0')
5298 address
= camel_internet_address_new ();
5299 if (camel_address_unformat (CAMEL_ADDRESS (address
), reply_to
) == -1) {
5300 g_object_unref (address
);
5308 * e_msg_composer_get_raw_message_text_without_signature:
5310 * Returns the text/plain of the message from composer without signature
5313 e_msg_composer_get_raw_message_text_without_signature (EMsgComposer
*composer
)
5315 EHTMLEditor
*editor
;
5316 EContentEditor
*cnt_editor
;
5319 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5321 editor
= e_msg_composer_get_editor (composer
);
5322 cnt_editor
= e_html_editor_get_content_editor (editor
);
5324 content
= e_content_editor_get_content (
5326 E_CONTENT_EDITOR_GET_BODY
|
5327 E_CONTENT_EDITOR_GET_TEXT_PLAIN
|
5328 E_CONTENT_EDITOR_GET_EXCLUDE_SIGNATURE
,
5332 g_warning ("%s: Failed to retrieve content", G_STRFUNC
);
5333 content
= g_strdup ("");
5336 return g_byte_array_new_take ((guint8
*) content
, strlen (content
));
5340 * e_msg_composer_get_raw_message_text:
5342 * Returns the text/plain of the message from composer
5345 e_msg_composer_get_raw_message_text (EMsgComposer
*composer
)
5347 EHTMLEditor
*editor
;
5348 EContentEditor
*cnt_editor
;
5351 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5353 editor
= e_msg_composer_get_editor (composer
);
5354 cnt_editor
= e_html_editor_get_content_editor (editor
);
5356 content
= e_content_editor_get_content (
5358 E_CONTENT_EDITOR_GET_BODY
|
5359 E_CONTENT_EDITOR_GET_TEXT_PLAIN
,
5363 g_warning ("%s: Failed to retrieve content", G_STRFUNC
);
5364 content
= g_strdup ("");
5367 return g_byte_array_new_take ((guint8
*) content
, strlen (content
));
5371 e_msg_composer_is_exiting (EMsgComposer
*composer
)
5373 g_return_val_if_fail (composer
!= NULL
, FALSE
);
5375 return composer
->priv
->application_exiting
;
5379 e_msg_composer_request_close (EMsgComposer
*composer
)
5381 g_return_if_fail (composer
!= NULL
);
5383 composer
->priv
->application_exiting
= TRUE
;
5386 /* Returns whether can close the composer immediately. It will return FALSE
5387 * also when saving to drafts, but the e_msg_composer_is_exiting will return
5388 * TRUE for this case. can_save_draft means whether can save draft
5389 * immediately, or rather keep it on the caller (when FALSE). If kept on the
5390 * folder, then returns FALSE and sets interval variable to return TRUE in
5391 * e_msg_composer_is_exiting. */
5393 e_msg_composer_can_close (EMsgComposer
*composer
,
5394 gboolean can_save_draft
)
5396 gboolean res
= FALSE
;
5397 EHTMLEditor
*editor
;
5398 EContentEditor
*cnt_editor
;
5399 EComposerHeaderTable
*table
;
5402 const gchar
*subject
, *message_name
;
5405 widget
= GTK_WIDGET (composer
);
5406 editor
= e_msg_composer_get_editor (composer
);
5407 cnt_editor
= e_html_editor_get_content_editor (editor
);
5409 /* this means that there is an async operation running,
5410 * in which case the composer cannot be closed */
5411 if (!gtk_action_group_get_sensitive (composer
->priv
->async_actions
))
5414 if (!e_content_editor_get_changed (cnt_editor
) ||
5415 e_content_editor_is_malfunction (cnt_editor
))
5418 window
= gtk_widget_get_window (widget
);
5419 gdk_window_raise (window
);
5421 table
= e_msg_composer_get_header_table (composer
);
5422 subject
= e_composer_header_table_get_subject (table
);
5424 if (subject
== NULL
|| *subject
== '\0')
5425 message_name
= "mail-composer:exit-unsaved-no-subject";
5427 message_name
= "mail-composer:exit-unsaved";
5429 response
= e_alert_run_dialog_for_args (
5430 GTK_WINDOW (composer
),
5435 case GTK_RESPONSE_YES
:
5436 e_msg_composer_request_close (composer
);
5438 gtk_action_activate (ACTION (SAVE_DRAFT
));
5441 case GTK_RESPONSE_NO
:
5445 case GTK_RESPONSE_CANCEL
:
5452 EComposerHeaderTable
*
5453 e_msg_composer_get_header_table (EMsgComposer
*composer
)
5455 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5457 return E_COMPOSER_HEADER_TABLE (composer
->priv
->header_table
);
5461 e_msg_composer_get_attachment_view (EMsgComposer
*composer
)
5463 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
5465 return E_ATTACHMENT_VIEW (composer
->priv
->attachment_paned
);
5469 e_save_spell_languages (const GList
*spell_dicts
)
5471 GSettings
*settings
;
5472 GPtrArray
*lang_array
;
5474 /* Build a list of spell check language codes. */
5475 lang_array
= g_ptr_array_new ();
5477 while (spell_dicts
!= NULL
) {
5478 ESpellDictionary
*dict
= spell_dicts
->data
;
5479 const gchar
*language_code
;
5481 language_code
= e_spell_dictionary_get_code (dict
);
5482 g_ptr_array_add (lang_array
, (gpointer
) language_code
);
5484 spell_dicts
= g_list_next (spell_dicts
);
5487 g_ptr_array_add (lang_array
, NULL
);
5489 /* Save the language codes to GSettings. */
5490 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
5491 g_settings_set_strv (
5492 settings
, "composer-spell-languages",
5493 (const gchar
* const *) lang_array
->pdata
);
5494 g_object_unref (settings
);
5496 g_ptr_array_free (lang_array
, TRUE
);
5500 e_msg_composer_save_focused_widget (EMsgComposer
*composer
)
5504 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5506 widget
= gtk_window_get_focus (GTK_WINDOW (composer
));
5507 composer
->priv
->focused_entry
= widget
;
5509 if (E_IS_CONTENT_EDITOR (widget
))
5510 e_content_editor_selection_save (E_CONTENT_EDITOR (widget
));
5512 if (GTK_IS_EDITABLE (widget
)) {
5513 gtk_editable_get_selection_bounds (
5514 GTK_EDITABLE (widget
),
5515 &composer
->priv
->focused_entry_selection_start
,
5516 &composer
->priv
->focused_entry_selection_end
);
5521 e_msg_composer_restore_focus_on_composer (EMsgComposer
*composer
)
5523 GtkWidget
*widget
= composer
->priv
->focused_entry
;
5525 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5530 gtk_window_set_focus (GTK_WINDOW (composer
), widget
);
5532 if (GTK_IS_EDITABLE (widget
)) {
5533 gtk_editable_select_region (
5534 GTK_EDITABLE (widget
),
5535 composer
->priv
->focused_entry_selection_start
,
5536 composer
->priv
->focused_entry_selection_end
);
5539 if (E_IS_CONTENT_EDITOR (widget
)) {
5540 EContentEditor
*cnt_editor
= E_CONTENT_EDITOR (widget
);
5541 e_content_editor_selection_restore (cnt_editor
);
5544 composer
->priv
->focused_entry
= NULL
;
5548 e_msg_composer_get_is_reply_or_forward (EMsgComposer
*composer
)
5550 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), FALSE
);
5552 return composer
->priv
->is_reply_or_forward
;
5556 e_msg_composer_set_is_reply_or_forward (EMsgComposer
*composer
,
5557 gboolean is_reply_or_forward
)
5559 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
5561 if ((composer
->priv
->is_reply_or_forward
? 1 : 0) == (is_reply_or_forward
? 1 : 0))
5564 composer
->priv
->is_reply_or_forward
= is_reply_or_forward
;
5566 g_object_notify (G_OBJECT (composer
), "is-reply-or-forward");
5568 msg_composer_mail_identity_changed_cb (composer
);