1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 *
17 * Authors:
18 * Jeffrey Stedfast <fejj@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #include "evolution-config.h"
25
26 #include <locale.h>
27 #include <string.h>
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30
31 #include <e-util/e-util.h>
32
33 #include <libemail-engine/libemail-engine.h>
34
35 #include <em-format/e-mail-parser.h>
36 #include <em-format/e-mail-part-utils.h>
37 #include <em-format/e-mail-formatter-quote.h>
38
39 #include <shell/e-shell.h>
40
41 #include <composer/e-msg-composer.h>
42 #include <composer/e-composer-actions.h>
43 #include <composer/e-composer-post-header.h>
44
45 #include "e-mail-printer.h"
46 #include "e-mail-ui-session.h"
47 #include "e-mail-templates.h"
48 #include "e-mail-templates-store.h"
49 #include "em-utils.h"
50 #include "em-composer-utils.h"
51 #include "em-folder-selector.h"
52 #include "em-folder-tree.h"
53 #include "em-event.h"
54 #include "mail-send-recv.h"
55
56 #ifdef G_OS_WIN32
57 #ifdef gmtime_r
58 #undef gmtime_r
59 #endif
60 #ifdef localtime_r
61 #undef localtime_r
62 #endif
63
64 /* The gmtime() and localtime() in Microsoft's C library are MT-safe */
65 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
66 #define localtime_r(tp,tmp) (localtime(tp)?(*(tmp)=*localtime(tp),(tmp)):0)
67 #endif
68
69 typedef struct _AsyncContext AsyncContext;
70 typedef struct _ForwardData ForwardData;
71
72 struct _AsyncContext {
73 CamelMimeMessage *message;
74 EMailSession *session;
75 EMsgComposer *composer;
76 ESource *transport_source;
77 EActivity *activity;
78 gchar *folder_uri;
79 gchar *message_uid;
80 gulong num_loading_handler_id;
81 gulong cancelled_handler_id;
82 };
83
84 struct _ForwardData {
85 EShell *shell;
86 CamelFolder *folder;
87 GPtrArray *uids;
88 EMailForwardStyle style;
89 };
90
91 static void
async_context_free(AsyncContext * async_context)92 async_context_free (AsyncContext *async_context)
93 {
94 if (async_context->cancelled_handler_id) {
95 GCancellable *cancellable;
96
97 cancellable = e_activity_get_cancellable (async_context->activity);
98 /* Cannot use g_cancellable_disconnect(), because when this is called
99 from inside the cancelled handler, then the GCancellable deadlocks. */
100 g_signal_handler_disconnect (cancellable, async_context->cancelled_handler_id);
101 async_context->cancelled_handler_id = 0;
102 }
103
104 if (async_context->num_loading_handler_id) {
105 EAttachmentView *view;
106 EAttachmentStore *store;
107
108 view = e_msg_composer_get_attachment_view (async_context->composer);
109 store = e_attachment_view_get_store (view);
110
111 e_signal_disconnect_notify_handler (store, &async_context->num_loading_handler_id);
112 }
113
114 g_clear_object (&async_context->message);
115 g_clear_object (&async_context->session);
116 g_clear_object (&async_context->composer);
117 g_clear_object (&async_context->transport_source);
118 g_clear_object (&async_context->activity);
119
120 g_free (async_context->folder_uri);
121 g_free (async_context->message_uid);
122
123 g_slice_free (AsyncContext, async_context);
124 }
125
126 static void
forward_data_free(ForwardData * data)127 forward_data_free (ForwardData *data)
128 {
129 if (data->shell != NULL)
130 g_object_unref (data->shell);
131
132 if (data->folder != NULL)
133 g_object_unref (data->folder);
134
135 if (data->uids != NULL)
136 g_ptr_array_unref (data->uids);
137
138 g_slice_free (ForwardData, data);
139 }
140
141 static gboolean
ask_confirm_for_unwanted_html_mail(EMsgComposer * composer,EDestination ** recipients)142 ask_confirm_for_unwanted_html_mail (EMsgComposer *composer,
143 EDestination **recipients)
144 {
145 gboolean res;
146 GString *str;
147 gint i;
148
149 str = g_string_new ("");
150 for (i = 0; recipients[i] != NULL; ++i) {
151 if (!e_destination_get_html_mail_pref (recipients[i])) {
152 const gchar *name;
153
154 name = e_destination_get_textrep (recipients[i], FALSE);
155
156 g_string_append_printf (str, " %s\n", name);
157 }
158 }
159
160 if (str->len)
161 res = e_util_prompt_user (
162 GTK_WINDOW (composer),
163 "org.gnome.evolution.mail", "prompt-on-unwanted-html",
164 "mail:ask-send-html", str->str, NULL);
165 else
166 res = TRUE;
167
168 g_string_free (str, TRUE);
169
170 return res;
171 }
172
173 static gboolean
ask_confirm_for_empty_subject(EMsgComposer * composer)174 ask_confirm_for_empty_subject (EMsgComposer *composer)
175 {
176 return e_util_prompt_user (
177 GTK_WINDOW (composer),
178 "org.gnome.evolution.mail",
179 "prompt-on-empty-subject",
180 "mail:ask-send-no-subject", NULL);
181 }
182
183 static gboolean
ask_confirm_for_only_bcc(EMsgComposer * composer,gboolean hidden_list_case)184 ask_confirm_for_only_bcc (EMsgComposer *composer,
185 gboolean hidden_list_case)
186 {
187 /* If the user is mailing a hidden contact list, it is possible for
188 * them to create a message with only Bcc recipients without really
189 * realizing it. To try to avoid being totally confusing, I've changed
190 * this dialog to provide slightly different text in that case, to
191 * better explain what the hell is going on. */
192
193 return e_util_prompt_user (
194 GTK_WINDOW (composer),
195 "org.gnome.evolution.mail",
196 "prompt-on-only-bcc",
197 hidden_list_case ?
198 "mail:ask-send-only-bcc-contact" :
199 "mail:ask-send-only-bcc", NULL);
200 }
201
202 static gboolean
is_group_definition(const gchar * str)203 is_group_definition (const gchar *str)
204 {
205 const gchar *colon;
206
207 if (!str || !*str)
208 return FALSE;
209
210 colon = strchr (str, ':');
211 return colon > str && strchr (str, ';') > colon;
212 }
213
214 static gboolean
composer_presend_check_recipients(EMsgComposer * composer,EMailSession * session)215 composer_presend_check_recipients (EMsgComposer *composer,
216 EMailSession *session)
217 {
218 EDestination **recipients;
219 EDestination **recipients_bcc;
220 CamelInternetAddress *cia;
221 EComposerHeaderTable *table;
222 EComposerHeader *post_to_header;
223 GString *invalid_addrs = NULL;
224 GSettings *settings;
225 gboolean check_passed = FALSE;
226 gint hidden = 0;
227 gint shown = 0;
228 gint num = 0;
229 gint num_to_cc = 0;
230 gint num_bcc = 0;
231 gint num_post = 0;
232 gint ii;
233
234 /* We should do all of the validity checks based on the composer,
235 * and not on the created message, as extra interaction may occur
236 * when we get the message (e.g. passphrase to sign a message). */
237
238 table = e_msg_composer_get_header_table (composer);
239
240 recipients = e_composer_header_table_get_destinations_to (table);
241 if (recipients) {
242 for (ii = 0; recipients[ii] != NULL; ii++) {
243 const gchar *addr;
244
245 addr = e_destination_get_address (recipients[ii]);
246 if (addr == NULL || *addr == '\0')
247 continue;
248
249 num_to_cc++;
250 }
251
252 e_destination_freev (recipients);
253 }
254
255 recipients = e_composer_header_table_get_destinations_cc (table);
256 if (recipients) {
257 for (ii = 0; recipients[ii] != NULL; ii++) {
258 const gchar *addr;
259
260 addr = e_destination_get_address (recipients[ii]);
261 if (addr == NULL || *addr == '\0')
262 continue;
263
264 num_to_cc++;
265 }
266
267 e_destination_freev (recipients);
268 }
269
270 recipients = e_composer_header_table_get_destinations (table);
271
272 cia = camel_internet_address_new ();
273
274 /* See which ones are visible, present, etc. */
275 for (ii = 0; recipients != NULL && recipients[ii] != NULL; ii++) {
276 const gchar *addr;
277 gint len, j;
278
279 addr = e_destination_get_address (recipients[ii]);
280 if (addr == NULL || *addr == '\0')
281 continue;
282
283 camel_address_decode (CAMEL_ADDRESS (cia), addr);
284 len = camel_address_length (CAMEL_ADDRESS (cia));
285
286 if (len > 0) {
287 if (!e_destination_is_evolution_list (recipients[ii])) {
288 for (j = 0; j < len; j++) {
289 const gchar *name = NULL, *eml = NULL;
290
291 if (!camel_internet_address_get (cia, j, &name, &eml) ||
292 !eml ||
293 strchr (eml, '@') <= eml) {
294 if (!invalid_addrs)
295 invalid_addrs = g_string_new ("");
296 else
297 g_string_append (invalid_addrs, ", ");
298
299 if (name)
300 g_string_append (invalid_addrs, name);
301 if (eml) {
302 g_string_append (invalid_addrs, name ? " <" : "");
303 g_string_append (invalid_addrs, eml);
304 g_string_append (invalid_addrs, name ? ">" : "");
305 }
306 }
307 }
308 }
309
310 camel_address_remove (CAMEL_ADDRESS (cia), -1);
311 num++;
312 if (e_destination_is_evolution_list (recipients[ii])
313 && !e_destination_list_show_addresses (recipients[ii])) {
314 hidden++;
315 } else {
316 shown++;
317 }
318 } else if (is_group_definition (addr)) {
319 /* like an address, it will not claim on only-bcc */
320 shown++;
321 num++;
322 } else if (!invalid_addrs) {
323 invalid_addrs = g_string_new (addr);
324 } else {
325 g_string_append (invalid_addrs, ", ");
326 g_string_append (invalid_addrs, addr);
327 }
328 }
329
330 recipients_bcc = e_composer_header_table_get_destinations_bcc (table);
331 if (recipients_bcc) {
332 for (ii = 0; recipients_bcc[ii] != NULL; ii++) {
333 const gchar *addr;
334
335 addr = e_destination_get_address (recipients_bcc[ii]);
336 if (addr == NULL || *addr == '\0')
337 continue;
338
339 camel_address_decode (CAMEL_ADDRESS (cia), addr);
340 if (camel_address_length (CAMEL_ADDRESS (cia)) > 0) {
341 camel_address_remove (CAMEL_ADDRESS (cia), -1);
342 num_bcc++;
343 }
344 }
345
346 e_destination_freev (recipients_bcc);
347 }
348
349 g_object_unref (cia);
350
351 post_to_header = e_composer_header_table_get_header (
352 table, E_COMPOSER_HEADER_POST_TO);
353 if (e_composer_header_get_visible (post_to_header)) {
354 GList *postlist;
355
356 postlist = e_composer_header_table_get_post_to (table);
357 num_post = g_list_length (postlist);
358 g_list_foreach (postlist, (GFunc) g_free, NULL);
359 g_list_free (postlist);
360 }
361
362 /* I'm sensing a lack of love, er, I mean recipients. */
363 if (num == 0 && num_post == 0) {
364 EHTMLEditor *editor;
365
366 editor = e_msg_composer_get_editor (composer);
367 e_alert_submit (E_ALERT_SINK (editor), "mail:send-no-recipients", NULL);
368
369 goto finished;
370 }
371
372 if (invalid_addrs) {
373 if (!e_util_prompt_user (
374 GTK_WINDOW (composer),
375 "org.gnome.evolution.mail",
376 "prompt-on-invalid-recip",
377 strstr (invalid_addrs->str, ", ") ?
378 "mail:ask-send-invalid-recip-multi" :
379 "mail:ask-send-invalid-recip-one",
380 invalid_addrs->str, NULL)) {
381 g_string_free (invalid_addrs, TRUE);
382 goto finished;
383 }
384
385 g_string_free (invalid_addrs, TRUE);
386 }
387
388 settings = e_util_ref_settings ("org.gnome.evolution.mail");
389 if (num_to_cc > 1 && num_to_cc >= g_settings_get_int (settings, "composer-many-to-cc-recips-num")) {
390 gchar *head;
391 gchar *msg;
392
393 g_clear_object (&settings);
394
395 head = g_strdup_printf (ngettext (
396 /* Translators: The %d is replaced with the actual count of recipients, which is always more than one. */
397 "Are you sure you want to send a message with %d To and CC recipients?",
398 "Are you sure you want to send a message with %d To and CC recipients?",
399 num_to_cc), num_to_cc);
400
401 msg = g_strdup_printf (ngettext (
402 /* Translators: The %d is replaced with the actual count of recipients, which is always more than one. */
403 "You are trying to send a message to %d recipients in To and CC fields."
404 " This would result in all recipients seeing the email addresses of each"
405 " other. In some cases this behaviour is undesired, especially if they"
406 " do not know each other or if privacy is a concern. Consider adding"
407 " recipients to the BCC field instead.",
408 "You are trying to send a message to %d recipients in To and CC fields."
409 " This would result in all recipients seeing the email addresses of each"
410 " other. In some cases this behaviour is undesired, especially if they"
411 " do not know each other or if privacy is a concern. Consider adding"
412 " recipients to the BCC field instead.",
413 num_to_cc), num_to_cc);
414
415 if (!e_util_prompt_user (
416 GTK_WINDOW (composer),
417 "org.gnome.evolution.mail",
418 "prompt-on-many-to-cc-recips",
419 "mail:ask-many-to-cc-recips",
420 head, msg, NULL)) {
421 GtkAction *action;
422
423 g_free (head);
424 g_free (msg);
425
426 action = E_COMPOSER_ACTION_VIEW_BCC (composer);
427 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
428
429 goto finished;
430 }
431
432 g_free (head);
433 g_free (msg);
434 }
435 g_clear_object (&settings);
436
437 if (num > 0 && (num == num_bcc || shown == 0)) {
438 /* this means that the only recipients are Bcc's */
439 if (!ask_confirm_for_only_bcc (composer, shown == 0))
440 goto finished;
441 }
442
443 check_passed = TRUE;
444
445 finished:
446 if (recipients != NULL)
447 e_destination_freev (recipients);
448
449 return check_passed;
450 }
451
452 static gboolean
composer_presend_check_identity(EMsgComposer * composer,EMailSession * session)453 composer_presend_check_identity (EMsgComposer *composer,
454 EMailSession *session)
455 {
456 EComposerHeaderTable *table;
457 ESource *source = NULL;
458 gchar *uid;
459 gboolean success = TRUE;
460
461 table = e_msg_composer_get_header_table (composer);
462
463 uid = e_composer_header_table_dup_identity_uid (table, NULL, NULL);
464 if (uid)
465 source = e_composer_header_table_ref_source (table, uid);
466 g_free (uid);
467
468 if (source) {
469 EClientCache *client_cache;
470 ESourceRegistry *registry;
471
472 client_cache = e_composer_header_table_ref_client_cache (table);
473 registry = e_client_cache_ref_registry (client_cache);
474
475 success = e_source_registry_check_enabled (registry, source);
476 if (!success) {
477 e_alert_submit (
478 E_ALERT_SINK (e_msg_composer_get_editor (composer)),
479 "mail:send-no-account-enabled", NULL);
480 }
481
482 g_object_unref (client_cache);
483 g_object_unref (registry);
484 } else {
485 success = FALSE;
486 e_alert_submit (
487 E_ALERT_SINK (e_msg_composer_get_editor (composer)),
488 "mail:send-no-account", NULL);
489 }
490
491
492 g_clear_object (&source);
493
494 return success;
495 }
496
497 static gboolean
composer_presend_check_plugins(EMsgComposer * composer,EMailSession * session)498 composer_presend_check_plugins (EMsgComposer *composer,
499 EMailSession *session)
500 {
501 EMEvent *eme;
502 EMEventTargetComposer *target;
503 gpointer data;
504
505 /** @Event: composer.presendchecks
506 * @Title: Composer PreSend Checks
507 * @Target: EMEventTargetMessage
508 *
509 * composer.presendchecks is emitted during pre-checks for the
510 * message just before sending. Since the e-plugin framework
511 * doesn't provide a way to return a value from the plugin,
512 * use 'presend_check_status' to set whether the check passed.
513 */
514 eme = em_event_peek ();
515 target = em_event_target_new_composer (eme, composer, 0);
516
517 e_event_emit (
518 (EEvent *) eme, "composer.presendchecks",
519 (EEventTarget *) target);
520
521 /* A non-NULL value for this key means the check failed. */
522 data = g_object_get_data (G_OBJECT (composer), "presend_check_status");
523
524 /* Clear the value in case we have to run these checks again. */
525 g_object_set_data (G_OBJECT (composer), "presend_check_status", NULL);
526
527 return (data == NULL);
528 }
529
530 static gboolean
composer_presend_check_subject(EMsgComposer * composer,EMailSession * session)531 composer_presend_check_subject (EMsgComposer *composer,
532 EMailSession *session)
533 {
534 EComposerHeaderTable *table;
535 const gchar *subject;
536 gboolean check_passed = TRUE;
537
538 table = e_msg_composer_get_header_table (composer);
539 subject = e_composer_header_table_get_subject (table);
540
541 if (subject == NULL || subject[0] == '\0') {
542 if (!ask_confirm_for_empty_subject (composer))
543 check_passed = FALSE;
544 }
545
546 return check_passed;
547 }
548
549 static gboolean
composer_presend_check_unwanted_html(EMsgComposer * composer,EMailSession * session)550 composer_presend_check_unwanted_html (EMsgComposer *composer,
551 EMailSession *session)
552 {
553 EDestination **recipients;
554 EHTMLEditor *editor;
555 EContentEditor *cnt_editor;
556 EComposerHeaderTable *table;
557 GSettings *settings;
558 gboolean check_passed = TRUE;
559 gboolean html_mode;
560 gboolean send_html;
561 gboolean confirm_html;
562 gint ii;
563
564 settings = e_util_ref_settings ("org.gnome.evolution.mail");
565
566 editor = e_msg_composer_get_editor (composer);
567 cnt_editor = e_html_editor_get_content_editor (editor);
568 html_mode = e_content_editor_get_html_mode (cnt_editor);
569
570 table = e_msg_composer_get_header_table (composer);
571 recipients = e_composer_header_table_get_destinations (table);
572
573 send_html = g_settings_get_boolean (settings, "composer-send-html");
574 confirm_html = g_settings_get_boolean (settings, "prompt-on-unwanted-html");
575
576 /* Only show this warning if our default is to send html. If it
577 * isn't, we've manually switched into html mode in the composer
578 * and (presumably) had a good reason for doing this. */
579 if (html_mode && send_html && confirm_html && recipients != NULL) {
580 gboolean html_problem = FALSE;
581
582 for (ii = 0; recipients[ii] != NULL; ii++) {
583 if (!e_destination_get_html_mail_pref (recipients[ii])) {
584 html_problem = TRUE;
585 break;
586 }
587 }
588
589 if (html_problem) {
590 if (!ask_confirm_for_unwanted_html_mail (
591 composer, recipients))
592 check_passed = FALSE;
593 }
594 }
595
596 if (recipients != NULL)
597 e_destination_freev (recipients);
598
599 g_object_unref (settings);
600
601 return check_passed;
602 }
603
604 static void
composer_send_completed(GObject * source_object,GAsyncResult * result,gpointer user_data)605 composer_send_completed (GObject *source_object,
606 GAsyncResult *result,
607 gpointer user_data)
608 {
609 EActivity *activity;
610 gboolean service_unavailable;
611 gboolean set_changed = FALSE;
612 AsyncContext *async_context;
613 GError *local_error = NULL;
614
615 async_context = (AsyncContext *) user_data;
616
617 if (async_context->transport_source) {
618 EShell *shell;
619
620 shell = e_msg_composer_get_shell (async_context->composer);
621
622 e_shell_set_auth_prompt_parent (shell, async_context->transport_source, NULL);
623 }
624
625 activity = async_context->activity;
626
627 e_mail_session_send_to_finish (
628 E_MAIL_SESSION (source_object), result, &local_error);
629
630 if (e_activity_handle_cancellation (activity, local_error)) {
631 set_changed = TRUE;
632 goto exit;
633 }
634
635 /* Check for error codes which may indicate we're offline
636 * or name resolution failed or connection attempt failed. */
637 service_unavailable =
638 g_error_matches (
639 local_error, CAMEL_SERVICE_ERROR,
640 CAMEL_SERVICE_ERROR_UNAVAILABLE) ||
641 /* name resolution failed */
642 g_error_matches (
643 local_error, G_RESOLVER_ERROR,
644 G_RESOLVER_ERROR_NOT_FOUND) ||
645 g_error_matches (
646 local_error, G_RESOLVER_ERROR,
647 G_RESOLVER_ERROR_TEMPORARY_FAILURE) ||
648 /* something internal to Camel failed */
649 g_error_matches (
650 local_error, CAMEL_SERVICE_ERROR,
651 CAMEL_SERVICE_ERROR_URL_INVALID);
652 if (service_unavailable) {
653 /* Inform the user. */
654 e_alert_run_dialog_for_args (
655 GTK_WINDOW (async_context->composer),
656 "mail-composer:saving-to-outbox", NULL);
657 if (async_context->message)
658 g_signal_emit_by_name (
659 async_context->composer, "save-to-outbox",
660 async_context->message, activity);
661 else
662 e_msg_composer_save_to_outbox (async_context->composer);
663 goto exit;
664 }
665
666 /* Post-processing errors are shown in the shell window. */
667 if (g_error_matches (
668 local_error, E_MAIL_ERROR,
669 E_MAIL_ERROR_POST_PROCESSING)) {
670 EAlert *alert;
671 EShell *shell;
672
673 shell = e_msg_composer_get_shell (async_context->composer);
674
675 alert = e_alert_new (
676 "mail-composer:send-post-processing-error",
677 local_error->message, NULL);
678 e_shell_submit_alert (shell, alert);
679 g_object_unref (alert);
680
681 /* All other errors are shown in the composer window. */
682 } else if (local_error != NULL) {
683 gint response;
684
685 /* Clear the activity bar before
686 * presenting the error dialog. */
687 g_clear_object (&async_context->activity);
688 async_context->activity = e_html_editor_new_activity (e_msg_composer_get_editor (async_context->composer));
689 activity = async_context->activity;
690
691 response = e_alert_run_dialog_for_args (
692 GTK_WINDOW (async_context->composer),
693 "mail-composer:send-error",
694 local_error->message, NULL);
695 if (response == GTK_RESPONSE_OK) /* Try Again */
696 e_msg_composer_send (async_context->composer);
697 if (response == GTK_RESPONSE_ACCEPT) { /* Save to Outbox */
698 if (async_context->message)
699 g_signal_emit_by_name (
700 async_context->composer, "save-to-outbox",
701 async_context->message, activity);
702 else
703 e_msg_composer_save_to_outbox (async_context->composer);
704 }
705 set_changed = TRUE;
706 goto exit;
707 }
708
709 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
710
711 /* Wait for the EActivity's completion message to
712 * time out and then destroy the composer window. */
713 g_object_weak_ref (
714 G_OBJECT (activity), (GWeakNotify)
715 gtk_widget_destroy, async_context->composer);
716
717 exit:
718 g_clear_error (&local_error);
719
720 if (set_changed) {
721 EHTMLEditor *editor;
722 EContentEditor *cnt_editor;
723
724 editor = e_msg_composer_get_editor (async_context->composer);
725 cnt_editor = e_html_editor_get_content_editor (editor);
726
727 e_content_editor_set_changed (cnt_editor, TRUE);
728
729 gtk_window_present (GTK_WINDOW (async_context->composer));
730 }
731
732 async_context_free (async_context);
733 }
734
735 static void
em_utils_composer_real_send(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,EMailSession * session)736 em_utils_composer_real_send (EMsgComposer *composer,
737 CamelMimeMessage *message,
738 EActivity *activity,
739 EMailSession *session)
740 {
741 AsyncContext *async_context;
742 CamelService *transport;
743 GCancellable *cancellable;
744 GSettings *settings;
745
746 settings = e_util_ref_settings ("org.gnome.evolution.mail");
747 if (g_settings_get_boolean (settings, "composer-use-outbox")) {
748 /* Using e_msg_composer_save_to_outbox() means building
749 the message again; better to use the built message here. */
750 g_signal_emit_by_name (
751 composer, "save-to-outbox",
752 message, activity);
753
754 g_object_unref (settings);
755 return;
756 }
757
758 g_object_unref (settings);
759
760 if (!camel_session_get_online (CAMEL_SESSION (session))) {
761 e_alert_run_dialog_for_args (
762 GTK_WINDOW (composer),
763 "mail-composer:saving-to-outbox", NULL);
764
765 /* Using e_msg_composer_save_to_outbox() means building
766 the message again; better to use the built message here. */
767 g_signal_emit_by_name (
768 composer, "save-to-outbox",
769 message, activity);
770
771 return;
772 }
773
774 async_context = g_slice_new0 (AsyncContext);
775 async_context->message = g_object_ref (message);
776 async_context->composer = g_object_ref (composer);
777 async_context->activity = g_object_ref (activity);
778
779 transport = e_mail_session_ref_transport_for_message (session, message);
780
781 if (transport) {
782 EShell *shell;
783
784 shell = e_msg_composer_get_shell (composer);
785
786 async_context->transport_source = e_source_registry_ref_source (e_shell_get_registry (shell), camel_service_get_uid (transport));
787
788 if (async_context->transport_source)
789 e_shell_set_auth_prompt_parent (shell, async_context->transport_source, GTK_WINDOW (composer));
790
791 g_object_unref (transport);
792 }
793
794 cancellable = e_activity_get_cancellable (activity);
795
796 e_mail_session_send_to (
797 session, message,
798 G_PRIORITY_DEFAULT,
799 cancellable, NULL, NULL,
800 composer_send_completed,
801 async_context);
802 }
803
804 static void
composer_num_loading_notify_cb(EAttachmentStore * store,GParamSpec * param,AsyncContext * async_context)805 composer_num_loading_notify_cb (EAttachmentStore *store,
806 GParamSpec *param,
807 AsyncContext *async_context)
808 {
809 if (e_attachment_store_get_num_loading (store) > 0)
810 return;
811
812 em_utils_composer_real_send (
813 async_context->composer,
814 async_context->message,
815 async_context->activity,
816 async_context->session);
817
818 async_context_free (async_context);
819 }
820
821 static void
composer_wait_for_attachment_load_cancelled_cb(GCancellable * cancellable,AsyncContext * async_context)822 composer_wait_for_attachment_load_cancelled_cb (GCancellable *cancellable,
823 AsyncContext *async_context)
824 {
825 async_context_free (async_context);
826 }
827
828 static void
em_utils_composer_send_cb(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,EMailSession * session)829 em_utils_composer_send_cb (EMsgComposer *composer,
830 CamelMimeMessage *message,
831 EActivity *activity,
832 EMailSession *session)
833 {
834 AsyncContext *async_context;
835 GCancellable *cancellable;
836 EAttachmentView *view;
837 EAttachmentStore *store;
838
839 view = e_msg_composer_get_attachment_view (composer);
840 store = e_attachment_view_get_store (view);
841
842 if (e_attachment_store_get_num_loading (store) <= 0) {
843 em_utils_composer_real_send (composer, message, activity, session);
844 return;
845 }
846
847 async_context = g_slice_new0 (AsyncContext);
848 async_context->session = g_object_ref (session);
849 async_context->message = g_object_ref (message);
850 async_context->composer = g_object_ref (composer);
851 async_context->activity = g_object_ref (activity);
852
853 cancellable = e_activity_get_cancellable (activity);
854 /* This message is never removed from the camel operation, otherwise the GtkInfoBar
855 hides itself and the user sees no feedback. */
856 camel_operation_push_message (cancellable, "%s", _("Waiting for attachments to load…"));
857
858 async_context->num_loading_handler_id = e_signal_connect_notify (store, "notify::num-loading",
859 G_CALLBACK (composer_num_loading_notify_cb), async_context);
860 /* Cannot use g_cancellable_connect() here, see async_context_free() */
861 async_context->cancelled_handler_id = g_signal_connect (cancellable, "cancelled",
862 G_CALLBACK (composer_wait_for_attachment_load_cancelled_cb), async_context);
863 }
864
865 static void
composer_set_no_change(EMsgComposer * composer)866 composer_set_no_change (EMsgComposer *composer)
867 {
868 EHTMLEditor *editor;
869 EContentEditor *cnt_editor;
870
871 g_return_if_fail (composer != NULL);
872
873 editor = e_msg_composer_get_editor (composer);
874 cnt_editor = e_html_editor_get_content_editor (editor);
875
876 e_content_editor_set_changed (cnt_editor, FALSE);
877 }
878
879 /* delete original messages from Outbox folder */
880 static void
manage_x_evolution_replace_outbox(EMsgComposer * composer,EMailSession * session,CamelMimeMessage * message,GCancellable * cancellable)881 manage_x_evolution_replace_outbox (EMsgComposer *composer,
882 EMailSession *session,
883 CamelMimeMessage *message,
884 GCancellable *cancellable)
885 {
886 const gchar *message_uid;
887 const gchar *header;
888 CamelFolder *outbox;
889
890 g_return_if_fail (composer != NULL);
891 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
892
893 header = "X-Evolution-Replace-Outbox-UID";
894 message_uid = camel_medium_get_header (CAMEL_MEDIUM (message), header);
895 e_msg_composer_remove_header (composer, header);
896
897 if (!message_uid)
898 return;
899
900 outbox = e_mail_session_get_local_folder (
901 session, E_MAIL_LOCAL_FOLDER_OUTBOX);
902 g_return_if_fail (outbox != NULL);
903
904 camel_folder_set_message_flags (
905 outbox, message_uid,
906 CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
907 CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
908
909 /* ignore errors here */
910 camel_folder_synchronize_message_sync (
911 outbox, message_uid, cancellable, NULL);
912 }
913
914 static void
composer_save_to_drafts_complete(GObject * source_object,GAsyncResult * result,gpointer user_data)915 composer_save_to_drafts_complete (GObject *source_object,
916 GAsyncResult *result,
917 gpointer user_data)
918 {
919 EActivity *activity;
920 AsyncContext *async_context;
921 EHTMLEditor *editor;
922 EContentEditor *cnt_editor;
923 GError *local_error = NULL;
924
925 async_context = (AsyncContext *) user_data;
926
927 editor = e_msg_composer_get_editor (async_context->composer);
928 cnt_editor = e_html_editor_get_content_editor (editor);
929
930 /* We don't really care if this failed. If something other than
931 * cancellation happened, emit a runtime warning so the error is
932 * not completely lost. */
933 e_mail_session_handle_draft_headers_finish (
934 E_MAIL_SESSION (source_object), result, &local_error);
935
936 activity = async_context->activity;
937
938 if (e_activity_handle_cancellation (activity, local_error)) {
939 e_content_editor_set_changed (cnt_editor, TRUE);
940 g_error_free (local_error);
941
942 } else if (local_error != NULL) {
943 e_content_editor_set_changed (cnt_editor, TRUE);
944 g_warning ("%s", local_error->message);
945 g_error_free (local_error);
946 } else
947 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
948
949 /* Encode the draft message we just saved into the EMsgComposer
950 * as X-Evolution-Draft headers. The message will be marked for
951 * deletion if the user saves a newer draft message or sends the
952 * composed message. */
953 e_msg_composer_set_draft_headers (
954 async_context->composer,
955 async_context->folder_uri,
956 async_context->message_uid);
957
958 e_content_editor_set_changed (cnt_editor, FALSE);
959
960 async_context_free (async_context);
961 }
962
963 static void
964 composer_save_to_drafts_append_mail (AsyncContext *async_context,
965 CamelFolder *drafts_folder);
966
967 static void
composer_save_to_drafts_cleanup(GObject * source_object,GAsyncResult * result,gpointer user_data)968 composer_save_to_drafts_cleanup (GObject *source_object,
969 GAsyncResult *result,
970 gpointer user_data)
971 {
972 CamelSession *session;
973 EActivity *activity;
974 EAlertSink *alert_sink;
975 GCancellable *cancellable;
976 EHTMLEditor *editor;
977 EContentEditor *cnt_editor;
978 AsyncContext *async_context;
979 GError *local_error = NULL;
980
981 async_context = (AsyncContext *) user_data;
982
983 editor = e_msg_composer_get_editor (async_context->composer);
984 cnt_editor = e_html_editor_get_content_editor (editor);
985
986 activity = async_context->activity;
987 alert_sink = e_activity_get_alert_sink (activity);
988 cancellable = e_activity_get_cancellable (activity);
989
990 e_mail_folder_append_message_finish (
991 CAMEL_FOLDER (source_object), result,
992 &async_context->message_uid, &local_error);
993
994 if (e_activity_handle_cancellation (activity, local_error)) {
995 g_warn_if_fail (async_context->message_uid == NULL);
996 e_content_editor_set_changed (cnt_editor, TRUE);
997 async_context_free (async_context);
998 g_error_free (local_error);
999 return;
1000
1001 } else if (local_error != NULL) {
1002 g_warn_if_fail (async_context->message_uid == NULL);
1003
1004 if (e_msg_composer_is_exiting (async_context->composer)) {
1005 gint response;
1006
1007 /* If we can't retrieve the Drafts folder for the
1008 * selected account, ask the user if he wants to
1009 * save to the local Drafts folder instead. */
1010 response = e_alert_run_dialog_for_args (
1011 GTK_WINDOW (async_context->composer),
1012 "mail:ask-default-drafts", local_error->message, NULL);
1013 if (response != GTK_RESPONSE_YES) {
1014 e_content_editor_set_changed (cnt_editor, TRUE);
1015 async_context_free (async_context);
1016 } else {
1017 composer_save_to_drafts_append_mail (async_context, NULL);
1018 }
1019
1020 g_error_free (local_error);
1021 return;
1022 }
1023
1024 e_alert_submit (
1025 alert_sink,
1026 "mail-composer:save-to-drafts-error",
1027 local_error->message, NULL);
1028 e_content_editor_set_changed (cnt_editor, TRUE);
1029 async_context_free (async_context);
1030 g_error_free (local_error);
1031 return;
1032 }
1033
1034 session = e_msg_composer_ref_session (async_context->composer);
1035
1036 /* Mark the previously saved draft message for deletion.
1037 * Note: This is just a nice-to-have; ignore failures. */
1038 e_mail_session_handle_draft_headers (
1039 E_MAIL_SESSION (session),
1040 async_context->message,
1041 G_PRIORITY_DEFAULT, cancellable,
1042 composer_save_to_drafts_complete,
1043 async_context);
1044
1045 g_object_unref (session);
1046 }
1047
1048 static void
composer_save_to_drafts_append_mail(AsyncContext * async_context,CamelFolder * drafts_folder)1049 composer_save_to_drafts_append_mail (AsyncContext *async_context,
1050 CamelFolder *drafts_folder)
1051 {
1052 CamelFolder *local_drafts_folder;
1053 GCancellable *cancellable;
1054 CamelMessageInfo *info;
1055
1056 local_drafts_folder =
1057 e_mail_session_get_local_folder (
1058 async_context->session, E_MAIL_LOCAL_FOLDER_DRAFTS);
1059
1060 if (drafts_folder == NULL)
1061 drafts_folder = g_object_ref (local_drafts_folder);
1062
1063 cancellable = e_activity_get_cancellable (async_context->activity);
1064
1065 info = camel_message_info_new (NULL);
1066
1067 camel_message_info_set_flags (info, CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_SEEN |
1068 (camel_mime_message_has_attachment (async_context->message) ? CAMEL_MESSAGE_ATTACHMENTS : 0), ~0);
1069
1070 camel_medium_remove_header (
1071 CAMEL_MEDIUM (async_context->message),
1072 "X-Evolution-Replace-Outbox-UID");
1073
1074 e_mail_folder_append_message (
1075 drafts_folder, async_context->message,
1076 info, G_PRIORITY_DEFAULT, cancellable,
1077 composer_save_to_drafts_cleanup,
1078 async_context);
1079
1080 g_clear_object (&info);
1081
1082 g_object_unref (drafts_folder);
1083 }
1084
1085 static void
composer_save_to_drafts_got_folder(GObject * source_object,GAsyncResult * result,gpointer user_data)1086 composer_save_to_drafts_got_folder (GObject *source_object,
1087 GAsyncResult *result,
1088 gpointer user_data)
1089 {
1090 EActivity *activity;
1091 CamelFolder *drafts_folder;
1092 EHTMLEditor *editor;
1093 EContentEditor *cnt_editor;
1094 AsyncContext *async_context;
1095 GError *local_error = NULL;
1096
1097 async_context = (AsyncContext *) user_data;
1098
1099 activity = async_context->activity;
1100
1101 editor = e_msg_composer_get_editor (async_context->composer);
1102 cnt_editor = e_html_editor_get_content_editor (editor);
1103
1104 drafts_folder = e_mail_session_uri_to_folder_finish (
1105 E_MAIL_SESSION (source_object), result, &local_error);
1106
1107 /* Sanity check. */
1108 g_return_if_fail (
1109 ((drafts_folder != NULL) && (local_error == NULL)) ||
1110 ((drafts_folder == NULL) && (local_error != NULL)));
1111
1112 if (e_activity_handle_cancellation (activity, local_error)) {
1113 e_content_editor_set_changed (cnt_editor, TRUE);
1114 async_context_free (async_context);
1115 g_error_free (local_error);
1116 return;
1117
1118 } else if (local_error != NULL) {
1119 gint response;
1120
1121 /* If we can't retrieve the Drafts folder for the
1122 * selected account, ask the user if he wants to
1123 * save to the local Drafts folder instead. */
1124 response = e_alert_run_dialog_for_args (
1125 GTK_WINDOW (async_context->composer),
1126 "mail:ask-default-drafts", local_error->message, NULL);
1127
1128 g_error_free (local_error);
1129
1130 if (response != GTK_RESPONSE_YES) {
1131 e_content_editor_set_changed (cnt_editor, TRUE);
1132 async_context_free (async_context);
1133 return;
1134 }
1135 }
1136
1137 composer_save_to_drafts_append_mail (async_context, drafts_folder);
1138 }
1139
1140 static void
em_utils_composer_save_to_drafts_cb(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,EMailSession * session)1141 em_utils_composer_save_to_drafts_cb (EMsgComposer *composer,
1142 CamelMimeMessage *message,
1143 EActivity *activity,
1144 EMailSession *session)
1145 {
1146 AsyncContext *async_context;
1147 EComposerHeaderTable *table;
1148 ESource *source;
1149 const gchar *local_drafts_folder_uri;
1150 gchar *identity_uid;
1151 gchar *drafts_folder_uri = NULL;
1152
1153 async_context = g_slice_new0 (AsyncContext);
1154 async_context->message = g_object_ref (message);
1155 async_context->session = g_object_ref (session);
1156 async_context->composer = g_object_ref (composer);
1157 async_context->activity = g_object_ref (activity);
1158
1159 table = e_msg_composer_get_header_table (composer);
1160
1161 identity_uid = e_composer_header_table_dup_identity_uid (table, NULL, NULL);
1162 source = e_composer_header_table_ref_source (table, identity_uid);
1163
1164 /* Get the selected identity's preferred Drafts folder. */
1165 if (source != NULL) {
1166 ESourceMailComposition *extension;
1167 const gchar *extension_name;
1168 gchar *uri;
1169
1170 extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
1171 extension = e_source_get_extension (source, extension_name);
1172 uri = e_source_mail_composition_dup_drafts_folder (extension);
1173
1174 drafts_folder_uri = uri;
1175
1176 g_object_unref (source);
1177 }
1178
1179 local_drafts_folder_uri =
1180 e_mail_session_get_local_folder_uri (
1181 session, E_MAIL_LOCAL_FOLDER_DRAFTS);
1182
1183 if (drafts_folder_uri == NULL) {
1184 async_context->folder_uri = g_strdup (local_drafts_folder_uri);
1185 composer_save_to_drafts_append_mail (async_context, NULL);
1186 } else {
1187 GCancellable *cancellable;
1188
1189 cancellable = e_activity_get_cancellable (activity);
1190 async_context->folder_uri = g_strdup (drafts_folder_uri);
1191
1192 e_mail_session_uri_to_folder (
1193 session, drafts_folder_uri, 0,
1194 G_PRIORITY_DEFAULT, cancellable,
1195 composer_save_to_drafts_got_folder,
1196 async_context);
1197
1198 g_free (drafts_folder_uri);
1199 }
1200
1201 g_free (identity_uid);
1202 }
1203
1204 static void
emcu_manage_flush_outbox(EMailSession * session)1205 emcu_manage_flush_outbox (EMailSession *session)
1206 {
1207 GSettings *settings;
1208
1209 g_return_if_fail (E_IS_MAIL_SESSION (session));
1210
1211 settings = e_util_ref_settings ("org.gnome.evolution.mail");
1212 if (g_settings_get_boolean (settings, "composer-use-outbox")) {
1213 gint delay_flush = g_settings_get_int (settings, "composer-delay-outbox-flush");
1214
1215 if (delay_flush == 0) {
1216 e_mail_session_flush_outbox (session);
1217 } else if (delay_flush > 0) {
1218 e_mail_session_schedule_outbox_flush (session, delay_flush);
1219 }
1220 }
1221 g_object_unref (settings);
1222 }
1223
1224 static void
composer_save_to_outbox_completed(GObject * source_object,GAsyncResult * result,gpointer user_data)1225 composer_save_to_outbox_completed (GObject *source_object,
1226 GAsyncResult *result,
1227 gpointer user_data)
1228 {
1229 EMailSession *session;
1230 EActivity *activity;
1231 EAlertSink *alert_sink;
1232 GCancellable *cancellable;
1233 AsyncContext *async_context;
1234 GError *local_error = NULL;
1235
1236 session = E_MAIL_SESSION (source_object);
1237 async_context = (AsyncContext *) user_data;
1238
1239 activity = async_context->activity;
1240 alert_sink = e_activity_get_alert_sink (activity);
1241 cancellable = e_activity_get_cancellable (activity);
1242
1243 e_mail_session_append_to_local_folder_finish (
1244 session, result, NULL, &local_error);
1245
1246 if (e_activity_handle_cancellation (activity, local_error)) {
1247 g_error_free (local_error);
1248 goto exit;
1249
1250 } else if (local_error != NULL) {
1251 e_alert_submit (
1252 alert_sink,
1253 "mail-composer:append-to-outbox-error",
1254 local_error->message, NULL);
1255 g_error_free (local_error);
1256 goto exit;
1257 }
1258
1259 /* special processing for Outbox folder */
1260 manage_x_evolution_replace_outbox (
1261 async_context->composer,
1262 session,
1263 async_context->message,
1264 cancellable);
1265
1266 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
1267
1268 /* Wait for the EActivity's completion message to
1269 * time out and then destroy the composer window. */
1270 g_object_weak_ref (
1271 G_OBJECT (activity), (GWeakNotify)
1272 gtk_widget_destroy, async_context->composer);
1273
1274 emcu_manage_flush_outbox (session);
1275
1276 exit:
1277 async_context_free (async_context);
1278 }
1279
1280 static void
em_utils_composer_save_to_outbox_cb(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,EMailSession * session)1281 em_utils_composer_save_to_outbox_cb (EMsgComposer *composer,
1282 CamelMimeMessage *message,
1283 EActivity *activity,
1284 EMailSession *session)
1285 {
1286 AsyncContext *async_context;
1287 CamelMessageInfo *info;
1288 GCancellable *cancellable;
1289
1290 async_context = g_slice_new0 (AsyncContext);
1291 async_context->message = g_object_ref (message);
1292 async_context->composer = g_object_ref (composer);
1293 async_context->activity = g_object_ref (activity);
1294
1295 cancellable = e_activity_get_cancellable (activity);
1296
1297 info = camel_message_info_new (NULL);
1298 camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
1299
1300 e_mail_session_append_to_local_folder (
1301 session, E_MAIL_LOCAL_FOLDER_OUTBOX,
1302 message, info, G_PRIORITY_DEFAULT, cancellable,
1303 composer_save_to_outbox_completed,
1304 async_context);
1305
1306 g_clear_object (&info);
1307 }
1308
1309 typedef struct _PrintAsyncContext {
1310 GMainLoop *main_loop;
1311 GError *error;
1312 } PrintAsyncContext;
1313
1314 static void
em_composer_utils_print_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1315 em_composer_utils_print_done_cb (GObject *source_object,
1316 GAsyncResult *result,
1317 gpointer user_data)
1318 {
1319 PrintAsyncContext *async_context = user_data;
1320
1321 g_return_if_fail (E_IS_MAIL_PRINTER (source_object));
1322 g_return_if_fail (async_context != NULL);
1323 g_return_if_fail (async_context->main_loop != NULL);
1324
1325 e_mail_printer_print_finish (E_MAIL_PRINTER (source_object), result, &(async_context->error));
1326
1327 g_main_loop_quit (async_context->main_loop);
1328 }
1329
1330 static void
em_utils_composer_print_cb(EMsgComposer * composer,GtkPrintOperationAction action,CamelMimeMessage * message,EActivity * activity,EMailSession * session)1331 em_utils_composer_print_cb (EMsgComposer *composer,
1332 GtkPrintOperationAction action,
1333 CamelMimeMessage *message,
1334 EActivity *activity,
1335 EMailSession *session)
1336 {
1337 EMailParser *parser;
1338 EMailPartList *parts, *reserved_parts;
1339 EMailPrinter *printer;
1340 EMailBackend *mail_backend;
1341 const gchar *message_id;
1342 GCancellable *cancellable;
1343 CamelObjectBag *parts_registry;
1344 gchar *mail_uri;
1345 PrintAsyncContext async_context;
1346
1347 mail_backend = E_MAIL_BACKEND (e_shell_get_backend_by_name (e_msg_composer_get_shell (composer), "mail"));
1348 g_return_if_fail (mail_backend != NULL);
1349
1350 cancellable = e_activity_get_cancellable (activity);
1351 parser = e_mail_parser_new (CAMEL_SESSION (session));
1352
1353 message_id = camel_mime_message_get_message_id (message);
1354 parts = e_mail_parser_parse_sync (parser, NULL, message_id, message, cancellable);
1355 if (!parts) {
1356 g_clear_object (&parser);
1357 return;
1358 }
1359
1360 parts_registry = e_mail_part_list_get_registry ();
1361
1362 mail_uri = e_mail_part_build_uri (NULL, message_id, NULL, NULL);
1363 reserved_parts = camel_object_bag_reserve (parts_registry, mail_uri);
1364 g_clear_object (&reserved_parts);
1365
1366 camel_object_bag_add (parts_registry, mail_uri, parts);
1367
1368 printer = e_mail_printer_new (parts, e_mail_backend_get_remote_content (mail_backend));
1369
1370 async_context.error = NULL;
1371 async_context.main_loop = g_main_loop_new (NULL, FALSE);
1372
1373 /* Cannot use EAsyncClosure here, it blocks the main context, which is not good here. */
1374 e_mail_printer_print (printer, action, NULL, cancellable, em_composer_utils_print_done_cb, &async_context);
1375
1376 g_main_loop_run (async_context.main_loop);
1377
1378 camel_object_bag_remove (parts_registry, parts);
1379 g_main_loop_unref (async_context.main_loop);
1380 g_object_unref (printer);
1381 g_object_unref (parts);
1382 g_free (mail_uri);
1383
1384 if (e_activity_handle_cancellation (activity, async_context.error)) {
1385 g_error_free (async_context.error);
1386 } else if (async_context.error != NULL) {
1387 e_alert_submit (
1388 e_activity_get_alert_sink (activity),
1389 "mail-composer:no-build-message",
1390 async_context.error->message, NULL);
1391 g_error_free (async_context.error);
1392 }
1393 }
1394
1395 static gint
compare_sources_with_uids_order_cb(gconstpointer a,gconstpointer b,gpointer user_data)1396 compare_sources_with_uids_order_cb (gconstpointer a,
1397 gconstpointer b,
1398 gpointer user_data)
1399 {
1400 ESource *asource = (ESource *) a;
1401 ESource *bsource = (ESource *) b;
1402 GHashTable *uids_order = user_data;
1403 gint aindex, bindex;
1404
1405 aindex = GPOINTER_TO_INT (g_hash_table_lookup (uids_order, e_source_get_uid (asource)));
1406 bindex = GPOINTER_TO_INT (g_hash_table_lookup (uids_order, e_source_get_uid (bsource)));
1407
1408 if (aindex <= 0)
1409 aindex = g_hash_table_size (uids_order);
1410 if (bindex <= 0)
1411 bindex = g_hash_table_size (uids_order);
1412
1413 return aindex - bindex;
1414 }
1415
1416 static void
sort_sources_by_ui(GList ** psources,gpointer user_data)1417 sort_sources_by_ui (GList **psources,
1418 gpointer user_data)
1419 {
1420 EShell *shell = user_data;
1421 EShellBackend *shell_backend;
1422 EMailSession *mail_session;
1423 EMailAccountStore *account_store;
1424 GtkTreeModel *model;
1425 GtkTreeIter iter;
1426 GHashTable *uids_order;
1427 gint index = 0;
1428
1429 g_return_if_fail (psources != NULL);
1430 g_return_if_fail (E_IS_SHELL (shell));
1431
1432 /* nothing to sort */
1433 if (!*psources || !g_list_next (*psources))
1434 return;
1435
1436 shell_backend = e_shell_get_backend_by_name (shell, "mail");
1437 g_return_if_fail (shell_backend != NULL);
1438
1439 mail_session = e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend));
1440 g_return_if_fail (mail_session != NULL);
1441
1442 account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (mail_session));
1443 g_return_if_fail (account_store != NULL);
1444
1445 model = GTK_TREE_MODEL (account_store);
1446 if (!gtk_tree_model_get_iter_first (model, &iter))
1447 return;
1448
1449 uids_order = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1450
1451 do {
1452 CamelService *service = NULL;
1453
1454 gtk_tree_model_get (model, &iter, E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE, &service, -1);
1455
1456 if (service) {
1457 index++;
1458 g_hash_table_insert (uids_order, g_strdup (camel_service_get_uid (service)), GINT_TO_POINTER (index));
1459 g_object_unref (service);
1460 }
1461 } while (gtk_tree_model_iter_next (model, &iter));
1462
1463 *psources = g_list_sort_with_data (*psources, compare_sources_with_uids_order_cb, uids_order);
1464
1465 g_hash_table_destroy (uids_order);
1466 }
1467
1468 /* (trasfer full) */
1469 ESource *
em_composer_utils_guess_identity_source(EShell * shell,CamelMimeMessage * message,CamelFolder * folder,const gchar * message_uid,gchar ** out_identity_name,gchar ** out_identity_address)1470 em_composer_utils_guess_identity_source (EShell *shell,
1471 CamelMimeMessage *message,
1472 CamelFolder *folder,
1473 const gchar *message_uid,
1474 gchar **out_identity_name,
1475 gchar **out_identity_address)
1476 {
1477 ESource *source;
1478
1479 g_return_val_if_fail (E_IS_SHELL (shell), NULL);
1480
1481 /* Check send account override for the passed-in folder */
1482 source = em_utils_check_send_account_override (shell, message, folder, out_identity_name, out_identity_address);
1483
1484 /* If not set and it's a search folder, then check the original folder */
1485 if (!source && message_uid && CAMEL_IS_VEE_FOLDER (folder)) {
1486 CamelMessageInfo *mi = camel_folder_get_message_info (folder, message_uid);
1487 if (mi) {
1488 CamelFolder *location;
1489
1490 location = camel_vee_folder_get_location (CAMEL_VEE_FOLDER (folder), (CamelVeeMessageInfo *) mi, NULL);
1491 if (location)
1492 source = em_utils_check_send_account_override (shell, message, location, out_identity_name, out_identity_address);
1493 g_clear_object (&mi);
1494 }
1495 }
1496
1497 /* If no send account override, then guess */
1498 if (!source) {
1499 source = em_utils_guess_mail_identity_with_recipients_and_sort (e_shell_get_registry (shell),
1500 message, folder, message_uid, out_identity_name, out_identity_address, sort_sources_by_ui, shell);
1501 }
1502
1503 return source;
1504 }
1505
1506 /* Composing messages... */
1507
1508 static CamelMimeMessage *em_utils_get_composer_recipients_as_message (EMsgComposer *composer);
1509
1510 static void
set_up_new_composer(EMsgComposer * composer,const gchar * subject,CamelFolder * folder,CamelMimeMessage * message,const gchar * message_uid,gboolean is_new_message)1511 set_up_new_composer (EMsgComposer *composer,
1512 const gchar *subject,
1513 CamelFolder *folder,
1514 CamelMimeMessage *message,
1515 const gchar *message_uid,
1516 gboolean is_new_message)
1517 {
1518 EClientCache *client_cache;
1519 ESourceRegistry *registry;
1520 EComposerHeaderTable *table;
1521 ESource *source = NULL;
1522 gchar *identity = NULL, *identity_name = NULL, *identity_address = NULL;
1523
1524 table = e_msg_composer_get_header_table (composer);
1525
1526 client_cache = e_composer_header_table_ref_client_cache (table);
1527 registry = e_client_cache_ref_registry (client_cache);
1528
1529 if (folder != NULL) {
1530 gchar *folder_uri;
1531 GList *list;
1532
1533 if (message) {
1534 g_object_ref (message);
1535 } else if (message_uid && !is_new_message) {
1536 /* Do this only if it's not a new message, in case the default account
1537 has set "always CC/Bcc" address, which might match one of the configured
1538 accounts, which in turn forces to use the default account due to this
1539 read back from the composer. */
1540 message = em_utils_get_composer_recipients_as_message (composer);
1541 }
1542
1543 if (message) {
1544 EShell *shell;
1545
1546 shell = e_msg_composer_get_shell (composer);
1547
1548 source = em_composer_utils_guess_identity_source (shell, message, folder, message_uid, &identity_name, &identity_address);
1549 }
1550
1551 /* In case of search folder, try to guess the store from
1552 the internal folders of it. If they are all from the same
1553 store, then use that store. */
1554 if (!source && CAMEL_IS_VEE_FOLDER (folder)) {
1555 GHashTable *stores, *done_folders;
1556 GSList *todo;
1557
1558 stores = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
1559 done_folders = g_hash_table_new (g_direct_hash, g_direct_equal);
1560
1561 todo = g_slist_prepend (NULL, g_object_ref (folder));
1562
1563 while (todo) {
1564 CamelVeeFolder *vfolder = todo->data;
1565
1566 todo = g_slist_remove (todo, vfolder);
1567 if (!g_hash_table_contains (done_folders, vfolder)) {
1568 GList *folders, *llink;
1569
1570 g_hash_table_insert (done_folders, vfolder, NULL);
1571
1572 folders = camel_vee_folder_ref_folders (vfolder);
1573 for (llink = folders; llink; llink = g_list_next (llink)) {
1574 CamelFolder *subfolder = llink->data;
1575
1576 if (!g_hash_table_contains (done_folders, subfolder)) {
1577 if (CAMEL_IS_VEE_FOLDER (subfolder)) {
1578 todo = g_slist_prepend (todo, g_object_ref (subfolder));
1579 } else {
1580 CamelStore *store = camel_folder_get_parent_store (subfolder);
1581
1582 g_hash_table_insert (done_folders, subfolder, NULL);
1583
1584 if (store) {
1585 g_hash_table_insert (stores, g_object_ref (store), NULL);
1586
1587 if (g_hash_table_size (stores) > 1) {
1588 g_slist_free_full (todo, g_object_unref);
1589 todo = NULL;
1590 break;
1591 }
1592 }
1593 }
1594 }
1595 }
1596
1597 g_list_free_full (folders, g_object_unref);
1598 }
1599
1600 g_object_unref (vfolder);
1601 }
1602
1603 if (g_hash_table_size (stores) == 1) {
1604 GHashTableIter iter;
1605 gpointer store;
1606
1607 g_hash_table_iter_init (&iter, stores);
1608 if (g_hash_table_iter_next (&iter, &store, NULL) && store)
1609 source = em_utils_ref_mail_identity_for_store (registry, store);
1610 }
1611
1612 g_slist_free_full (todo, g_object_unref);
1613 g_hash_table_destroy (done_folders);
1614 g_hash_table_destroy (stores);
1615 }
1616
1617 g_clear_object (&message);
1618
1619 if (!source) {
1620 CamelStore *store;
1621
1622 store = camel_folder_get_parent_store (folder);
1623 source = em_utils_ref_mail_identity_for_store (registry, store);
1624 }
1625
1626 folder_uri = e_mail_folder_uri_from_folder (folder);
1627
1628 list = g_list_prepend (NULL, folder_uri);
1629 e_composer_header_table_set_post_to_list (table, list);
1630 g_list_free (list);
1631
1632 g_free (folder_uri);
1633 }
1634
1635 if (source != NULL) {
1636 identity = e_source_dup_uid (source);
1637 g_object_unref (source);
1638 }
1639
1640 if (subject)
1641 e_composer_header_table_set_subject (table, subject);
1642 e_composer_header_table_set_identity_uid (table, identity, identity_name, identity_address);
1643
1644 em_utils_apply_send_account_override_to_composer (composer, folder);
1645
1646 g_free (identity);
1647 g_free (identity_name);
1648 g_free (identity_address);
1649
1650 g_object_unref (client_cache);
1651 g_object_unref (registry);
1652 }
1653
1654 /**
1655 * em_utils_compose_new_message:
1656 * @composer: an #EMsgComposer
1657 * @folder: (nullable): a #CamelFolder, or %NULL
1658 *
1659 * Sets up a new @composer window.
1660 *
1661 * See: em_utils_compose_new_message_with_selection()
1662 *
1663 * Since: 3.22
1664 **/
1665 void
em_utils_compose_new_message(EMsgComposer * composer,CamelFolder * folder)1666 em_utils_compose_new_message (EMsgComposer *composer,
1667 CamelFolder *folder)
1668 {
1669 em_utils_compose_new_message_with_selection (composer, folder, NULL);
1670 }
1671
1672 /**
1673 * em_utils_compose_new_message_with_selection:
1674 * @composer: an #EMsgComposer
1675 * @folder: (nullable): a #CamelFolder, or %NULL
1676 * @message_uid: (nullable): a UID of the selected message, or %NULL
1677 *
1678 * Sets up a new @composer window, similar to em_utils_compose_new_message(),
1679 * but also tries to identify From account more precisely, when the @folder
1680 * is a search folder.
1681 *
1682 * Since: 3.28
1683 **/
1684 void
em_utils_compose_new_message_with_selection(EMsgComposer * composer,CamelFolder * folder,const gchar * message_uid)1685 em_utils_compose_new_message_with_selection (EMsgComposer *composer,
1686 CamelFolder *folder,
1687 const gchar *message_uid)
1688 {
1689 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
1690
1691 if (folder)
1692 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1693
1694 set_up_new_composer (composer, "", folder, NULL, message_uid, TRUE);
1695 composer_set_no_change (composer);
1696
1697 gtk_widget_show (GTK_WIDGET (composer));
1698 }
1699
1700 static CamelMimeMessage *
em_utils_get_composer_recipients_as_message(EMsgComposer * composer)1701 em_utils_get_composer_recipients_as_message (EMsgComposer *composer)
1702 {
1703 CamelMimeMessage *message;
1704 EComposerHeaderTable *table;
1705 EComposerHeader *header;
1706 EDestination **destv;
1707 CamelInternetAddress *to_addr, *cc_addr, *bcc_addr, *dest_addr;
1708 const gchar *text_addr;
1709 gint ii;
1710
1711 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
1712
1713 table = e_msg_composer_get_header_table (composer);
1714 header = e_composer_header_table_get_header (table, E_COMPOSER_HEADER_TO);
1715
1716 if (!e_composer_header_get_visible (header))
1717 return NULL;
1718
1719 message = camel_mime_message_new ();
1720
1721 to_addr = camel_internet_address_new ();
1722 cc_addr = camel_internet_address_new ();
1723 bcc_addr = camel_internet_address_new ();
1724
1725 /* To */
1726 dest_addr = to_addr;
1727 destv = e_composer_header_table_get_destinations_to (table);
1728 for (ii = 0; destv != NULL && destv[ii] != NULL; ii++) {
1729 text_addr = e_destination_get_address (destv[ii]);
1730 if (text_addr && *text_addr) {
1731 if (camel_address_decode (CAMEL_ADDRESS (dest_addr), text_addr) <= 0)
1732 camel_internet_address_add (dest_addr, "", text_addr);
1733 }
1734 }
1735 e_destination_freev (destv);
1736
1737 /* CC */
1738 dest_addr = cc_addr;
1739 destv = e_composer_header_table_get_destinations_cc (table);
1740 for (ii = 0; destv != NULL && destv[ii] != NULL; ii++) {
1741 text_addr = e_destination_get_address (destv[ii]);
1742 if (text_addr && *text_addr) {
1743 if (camel_address_decode (CAMEL_ADDRESS (dest_addr), text_addr) <= 0)
1744 camel_internet_address_add (dest_addr, "", text_addr);
1745 }
1746 }
1747 e_destination_freev (destv);
1748
1749 /* Bcc */
1750 dest_addr = bcc_addr;
1751 destv = e_composer_header_table_get_destinations_bcc (table);
1752 for (ii = 0; destv != NULL && destv[ii] != NULL; ii++) {
1753 text_addr = e_destination_get_address (destv[ii]);
1754 if (text_addr && *text_addr) {
1755 if (camel_address_decode (CAMEL_ADDRESS (dest_addr), text_addr) <= 0)
1756 camel_internet_address_add (dest_addr, "", text_addr);
1757 }
1758 }
1759 e_destination_freev (destv);
1760
1761 if (camel_address_length (CAMEL_ADDRESS (to_addr)) > 0)
1762 camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_TO, to_addr);
1763
1764 if (camel_address_length (CAMEL_ADDRESS (cc_addr)) > 0)
1765 camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_CC, cc_addr);
1766
1767 if (camel_address_length (CAMEL_ADDRESS (bcc_addr)) > 0)
1768 camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_BCC, bcc_addr);
1769
1770 g_object_unref (to_addr);
1771 g_object_unref (cc_addr);
1772 g_object_unref (bcc_addr);
1773
1774 return message;
1775 }
1776
1777 typedef struct _CreateComposerData {
1778 CamelFolder *folder;
1779 const gchar *message_uid; /* In the Camel string pool */
1780 gchar *mailto;
1781 } CreateComposerData;
1782
1783 static void
create_composer_data_free(gpointer ptr)1784 create_composer_data_free (gpointer ptr)
1785 {
1786 CreateComposerData *ccd = ptr;
1787
1788 if (ccd) {
1789 g_clear_object (&ccd->folder);
1790 camel_pstring_free (ccd->message_uid);
1791 g_free (ccd->mailto);
1792 g_slice_free (CreateComposerData, ccd);
1793 }
1794 }
1795
1796 static void
msg_composer_created_with_mailto_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1797 msg_composer_created_with_mailto_cb (GObject *source_object,
1798 GAsyncResult *result,
1799 gpointer user_data)
1800 {
1801 CreateComposerData *ccd = user_data;
1802 EMsgComposer *composer;
1803 GError *error = NULL;
1804
1805 g_return_if_fail (ccd != NULL);
1806
1807 composer = e_msg_composer_new_finish (result, &error);
1808 if (error) {
1809 g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
1810 g_clear_error (&error);
1811 create_composer_data_free (ccd);
1812
1813 return;
1814 }
1815
1816 if (ccd->mailto)
1817 e_msg_composer_setup_from_url (composer, ccd->mailto);
1818
1819 set_up_new_composer (composer, NULL, ccd->folder, NULL, ccd->message_uid, TRUE);
1820
1821 composer_set_no_change (composer);
1822
1823 gtk_window_present (GTK_WINDOW (composer));
1824
1825 create_composer_data_free (ccd);
1826 }
1827
1828 /**
1829 * em_utils_compose_new_message_with_mailto:
1830 * @shell: an #EShell
1831 * @mailto: a mailto URL
1832 * @folder: a #CamelFolder, or %NULL
1833 *
1834 * Opens a new composer window as a child window of @parent's toplevel
1835 * window. If @mailto is non-NULL, the composer fields will be filled in
1836 * according to the values in the mailto URL.
1837 *
1838 * See: em_utils_compose_new_message_with_mailto_and_selection()
1839 **/
1840 void
em_utils_compose_new_message_with_mailto(EShell * shell,const gchar * mailto,CamelFolder * folder)1841 em_utils_compose_new_message_with_mailto (EShell *shell,
1842 const gchar *mailto,
1843 CamelFolder *folder)
1844 {
1845 em_utils_compose_new_message_with_mailto_and_selection (shell, mailto, folder, NULL);
1846 }
1847
1848 /**
1849 * em_utils_compose_new_message_with_mailto_and_selection:
1850 * @shell: an #EShell
1851 * @mailto: a mailto URL
1852 * @folder: a #CamelFolder, or %NULL
1853 * @message_uid: (nullable): a UID of the selected message, or %NULL
1854 *
1855 * similarly to em_utils_compose_new_message_with_mailto(), opens a new composer
1856 * window as a child window of @parent's toplevel window. If @mailto is non-NULL,
1857 * the composer fields will be filled in according to the values in the mailto URL.
1858 * It also tries to identify From account more precisely, when the @folder
1859 * is a search folder.
1860 *
1861 * Since: 3.28
1862 **/
1863 void
em_utils_compose_new_message_with_mailto_and_selection(EShell * shell,const gchar * mailto,CamelFolder * folder,const gchar * message_uid)1864 em_utils_compose_new_message_with_mailto_and_selection (EShell *shell,
1865 const gchar *mailto,
1866 CamelFolder *folder,
1867 const gchar *message_uid)
1868 {
1869 CreateComposerData *ccd;
1870
1871 g_return_if_fail (E_IS_SHELL (shell));
1872
1873 if (folder)
1874 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1875
1876 ccd = g_slice_new0 (CreateComposerData);
1877 ccd->folder = folder ? g_object_ref (folder) : NULL;
1878 ccd->message_uid = camel_pstring_strdup (message_uid);
1879 ccd->mailto = g_strdup (mailto);
1880
1881 e_msg_composer_new (shell, msg_composer_created_with_mailto_cb, ccd);
1882 }
1883
1884 static ESource *
emcu_ref_identity_source_from_composer(EMsgComposer * composer)1885 emcu_ref_identity_source_from_composer (EMsgComposer *composer)
1886 {
1887 EComposerHeaderTable *table;
1888 ESource *source = NULL;
1889 gchar *identity_uid;
1890
1891 if (!composer)
1892 return NULL;
1893
1894 table = e_msg_composer_get_header_table (composer);
1895 identity_uid = e_composer_header_table_dup_identity_uid (table, NULL, NULL);
1896 if (identity_uid)
1897 source = e_composer_header_table_ref_source (table, identity_uid);
1898 g_free (identity_uid);
1899
1900 return source;
1901 }
1902
1903 static void
emcu_change_locale(const gchar * lc_messages,const gchar * lc_time,gchar ** out_lc_messages,gchar ** out_lc_time)1904 emcu_change_locale (const gchar *lc_messages,
1905 const gchar *lc_time,
1906 gchar **out_lc_messages,
1907 gchar **out_lc_time)
1908 {
1909 gboolean success;
1910 gchar *previous;
1911
1912 if (lc_messages) {
1913 #if defined(LC_MESSAGES)
1914 previous = g_strdup (setlocale (LC_MESSAGES, NULL));
1915 success = setlocale (LC_MESSAGES, lc_messages) != NULL;
1916 #else
1917 previous = g_strdup (setlocale (LC_ALL, NULL));
1918 success = setlocale (LC_ALL, lc_messages) != NULL;
1919 #endif
1920
1921 if (out_lc_messages)
1922 *out_lc_messages = success ? g_strdup (previous) : NULL;
1923
1924 g_free (previous);
1925 }
1926
1927 if (lc_time) {
1928 #if defined(LC_TIME)
1929 previous = g_strdup (setlocale (LC_TIME, NULL));
1930 success = setlocale (LC_TIME, lc_time) != NULL;
1931 #elif defined(LC_MESSAGES)
1932 previous = g_strdup (setlocale (LC_ALL, NULL));
1933 success = setlocale (LC_ALL, lc_time) != NULL;
1934 #else
1935 previous = NULL;
1936 success = FALSE;
1937 #endif
1938
1939 if (out_lc_time)
1940 *out_lc_time = success ? g_strdup (previous) : NULL;
1941
1942 g_free (previous);
1943 }
1944 }
1945
1946 static void
emcu_prepare_attribution_locale(ESource * identity_source,gchar ** out_lc_messages,gchar ** out_lc_time)1947 emcu_prepare_attribution_locale (ESource *identity_source,
1948 gchar **out_lc_messages,
1949 gchar **out_lc_time)
1950 {
1951 gchar *lang = NULL;
1952
1953 g_return_if_fail (out_lc_messages != NULL);
1954 g_return_if_fail (out_lc_time != NULL);
1955
1956 if (identity_source && e_source_has_extension (identity_source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) {
1957 ESourceMailComposition *extension;
1958
1959 extension = e_source_get_extension (identity_source, E_SOURCE_EXTENSION_MAIL_COMPOSITION);
1960 lang = e_source_mail_composition_dup_language (extension);
1961 }
1962
1963 if (!lang || !*lang) {
1964 GSettings *settings;
1965
1966 g_free (lang);
1967
1968 settings = e_util_ref_settings ("org.gnome.evolution.mail");
1969 lang = g_settings_get_string (settings, "composer-attribution-language");
1970 g_object_unref (settings);
1971
1972 if (!lang || !*lang)
1973 g_clear_pointer (&lang, g_free);
1974 }
1975
1976 if (!lang) {
1977 /* Set the locale always, even when using the user interface
1978 language, because gettext() can return wrong text (in the previously
1979 used language) */
1980 #if defined(LC_MESSAGES)
1981 lang = g_strdup (setlocale (LC_MESSAGES, NULL));
1982 #else
1983 lang = g_strdup (setlocale (LC_ALL, NULL));
1984 #endif
1985 }
1986
1987 if (lang) {
1988 if (!g_str_equal (lang, "C") && !strchr (lang, '.')) {
1989 gchar *tmp;
1990
1991 tmp = g_strconcat (lang, ".UTF-8", NULL);
1992 g_free (lang);
1993 lang = tmp;
1994 }
1995
1996 emcu_change_locale (lang, lang, out_lc_messages, out_lc_time);
1997
1998 g_free (lang);
1999 }
2000 }
2001
2002 /* Takes ownership (frees) the 'restore_locale' */
2003 static void
emcu_restore_locale_after_attribution(gchar * restore_lc_messages,gchar * restore_lc_time)2004 emcu_restore_locale_after_attribution (gchar *restore_lc_messages,
2005 gchar *restore_lc_time)
2006 {
2007 emcu_change_locale (restore_lc_messages, restore_lc_time, NULL, NULL);
2008
2009 g_free (restore_lc_messages);
2010 g_free (restore_lc_time);
2011 }
2012
2013 /* Editing messages... */
2014
2015 typedef enum {
2016 QUOTING_ATTRIBUTION,
2017 QUOTING_FORWARD,
2018 QUOTING_ORIGINAL
2019 } QuotingTextEnum;
2020
2021 static struct {
2022 const gchar * conf_key;
2023 const gchar * message;
2024 } conf_messages[] = {
2025 [QUOTING_ATTRIBUTION] =
2026 { "composer-message-attribution",
2027 /* Note to translators: this is the attribution string used
2028 * when quoting messages. Each ${Variable} gets replaced
2029 * with a value. To see a full list of available variables,
2030 * see mail/em-composer-utils.c:attribvars array. */
2031 N_("On ${AbbrevWeekdayName}, ${Year}-${Month}-${Day} at "
2032 "${24Hour}:${Minute} ${TimeZone}, ${Sender} wrote:")
2033 },
2034
2035 [QUOTING_FORWARD] =
2036 { "composer-message-forward",
2037 N_("-------- Forwarded Message --------")
2038 },
2039
2040 [QUOTING_ORIGINAL] =
2041 { "composer-message-original",
2042 N_("-----Original Message-----")
2043 }
2044 };
2045
2046 static gchar *
quoting_text(QuotingTextEnum type,EMsgComposer * composer)2047 quoting_text (QuotingTextEnum type,
2048 EMsgComposer *composer)
2049 {
2050 GSettings *settings;
2051 gchar *restore_lc_messages = NULL, *restore_lc_time = NULL;
2052 gchar *text;
2053
2054 settings = e_util_ref_settings ("org.gnome.evolution.mail");
2055 text = g_settings_get_string (settings, conf_messages[type].conf_key);
2056 g_object_unref (settings);
2057
2058 if (text && *text)
2059 return text;
2060
2061 g_free (text);
2062
2063 if (composer) {
2064 ESource *identity_source;
2065
2066 identity_source = emcu_ref_identity_source_from_composer (composer);
2067
2068 emcu_prepare_attribution_locale (identity_source, &restore_lc_messages, &restore_lc_time);
2069
2070 g_clear_object (&identity_source);
2071 }
2072
2073 text = g_strdup (_(conf_messages[type].message));
2074
2075 emcu_restore_locale_after_attribution (restore_lc_messages, restore_lc_time);
2076
2077 return text;
2078 }
2079
2080 /**
2081 * em_composer_utils_get_forward_marker:
2082 * @composer: an #EMsgComposer from which to get the identity information
2083 *
2084 * Returns: (transfer full): a text marker which is used for inline forwarded messages.
2085 * Free returned pointer with g_free(), when no longer needed.
2086 *
2087 * Since: 3.24
2088 **/
2089 gchar *
em_composer_utils_get_forward_marker(EMsgComposer * composer)2090 em_composer_utils_get_forward_marker (EMsgComposer *composer)
2091 {
2092 return quoting_text (QUOTING_FORWARD, composer);
2093 }
2094
2095 /**
2096 * em_composer_utils_get_original_marker:
2097 * @composer: an #EMsgComposer from which to get the identity information
2098 *
2099 * Returns: (transfer full): a text marker which is used for inline message replies.
2100 * Free returned pointer with g_free(), when no longer needed.
2101 *
2102 * Since: 3.24
2103 **/
2104 gchar *
em_composer_utils_get_original_marker(EMsgComposer * composer)2105 em_composer_utils_get_original_marker (EMsgComposer *composer)
2106 {
2107 return quoting_text (QUOTING_ORIGINAL, composer);
2108 }
2109
2110 static gboolean
emcu_message_references_existing_account(CamelMimeMessage * message,EMsgComposer * composer)2111 emcu_message_references_existing_account (CamelMimeMessage *message,
2112 EMsgComposer *composer)
2113 {
2114 ESource *source;
2115 gchar *identity_uid;
2116 gboolean res = FALSE;
2117
2118 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
2119 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
2120
2121 identity_uid = (gchar *) camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Identity");
2122 if (!identity_uid) {
2123 /* for backward compatibility */
2124 identity_uid = (gchar *) camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Account");
2125 }
2126
2127 if (!identity_uid)
2128 return FALSE;
2129
2130 identity_uid = g_strstrip (g_strdup (identity_uid));
2131 source = e_composer_header_table_ref_source (e_msg_composer_get_header_table (composer), identity_uid);
2132
2133 res = source != NULL;
2134
2135 g_clear_object (&source);
2136 g_free (identity_uid);
2137
2138 return res;
2139 }
2140
2141 typedef struct _OutboxData {
2142 CamelSession *session;
2143 CamelMessageInfo *info;
2144 } OutboxData;
2145
2146 static void
outbox_data_free(gpointer ptr)2147 outbox_data_free (gpointer ptr)
2148 {
2149 OutboxData *od = ptr;
2150
2151 if (od) {
2152 if (od->info) {
2153 g_object_set_data (G_OBJECT (od->info), MAIL_USER_KEY_EDITING, NULL);
2154
2155 if (od->session && !(camel_message_info_get_flags (od->info) & CAMEL_MESSAGE_DELETED)) {
2156 emcu_manage_flush_outbox (E_MAIL_SESSION (od->session));
2157 }
2158 }
2159
2160 g_clear_object (&od->session);
2161 g_clear_object (&od->info);
2162 g_free (od);
2163 }
2164 }
2165
2166 /**
2167 * em_utils_edit_message:
2168 * @composer: an #EMsgComposer
2169 * @folder: a #CamelFolder
2170 * @message: a #CamelMimeMessage
2171 * @message_uid: UID of @message, or %NULL
2172 *
2173 * Sets up the @composer with the headers/mime-parts/etc of the @message.
2174 *
2175 * Since: 3.22
2176 **/
2177 void
em_utils_edit_message(EMsgComposer * composer,CamelFolder * folder,CamelMimeMessage * message,const gchar * message_uid,gboolean keep_signature)2178 em_utils_edit_message (EMsgComposer *composer,
2179 CamelFolder *folder,
2180 CamelMimeMessage *message,
2181 const gchar *message_uid,
2182 gboolean keep_signature)
2183 {
2184 ESourceRegistry *registry;
2185 ESource *source;
2186 gboolean folder_is_sent;
2187 gboolean folder_is_drafts;
2188 gboolean folder_is_outbox;
2189 gboolean folder_is_templates;
2190 gchar *override_identity_uid = NULL, *override_alias_name = NULL, *override_alias_address = NULL;
2191
2192 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2193 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2194 if (folder)
2195 g_return_if_fail (CAMEL_IS_FOLDER (folder));
2196
2197 registry = e_shell_get_registry (e_msg_composer_get_shell (composer));
2198
2199 if (folder) {
2200 folder_is_sent = em_utils_folder_is_sent (registry, folder);
2201 folder_is_drafts = em_utils_folder_is_drafts (registry, folder);
2202 folder_is_outbox = em_utils_folder_is_outbox (registry, folder);
2203 folder_is_templates = em_utils_folder_is_templates (registry, folder);
2204 } else {
2205 folder_is_sent = FALSE;
2206 folder_is_drafts = FALSE;
2207 folder_is_outbox = FALSE;
2208 folder_is_templates = FALSE;
2209 }
2210 if (folder) {
2211 if ((!folder_is_sent && !folder_is_drafts && !folder_is_outbox && !folder_is_templates) ||
2212 (!folder_is_outbox && !folder_is_templates && !emcu_message_references_existing_account (message, composer))) {
2213 CamelStore *store;
2214
2215 store = camel_folder_get_parent_store (folder);
2216 source = em_utils_ref_mail_identity_for_store (registry, store);
2217
2218 if (source) {
2219 g_free (override_identity_uid);
2220 override_identity_uid = e_source_dup_uid (source);
2221 g_object_unref (source);
2222 }
2223 }
2224
2225 source = em_utils_check_send_account_override (e_msg_composer_get_shell (composer), message, folder, &override_alias_name, &override_alias_address);
2226 if (source) {
2227 g_free (override_identity_uid);
2228 override_identity_uid = e_source_dup_uid (source);
2229 g_object_unref (source);
2230 }
2231 }
2232
2233 /* Remember the source message headers when re-editing in Drafts or Outbox,
2234 thus Reply, Forward and such mark the source message properly. Do this
2235 before the setup_with_message(), because it modifies the message headers. */
2236 if (folder_is_drafts || folder_is_outbox) {
2237 CamelMedium *medium;
2238 const gchar *hdr_folder;
2239 const gchar *hdr_message;
2240 const gchar *hdr_flags;
2241
2242 medium = CAMEL_MEDIUM (message);
2243
2244 hdr_folder = camel_medium_get_header (medium, "X-Evolution-Source-Folder");
2245 hdr_message = camel_medium_get_header (medium, "X-Evolution-Source-Message");
2246 hdr_flags = camel_medium_get_header (medium, "X-Evolution-Source-Flags");
2247
2248 if (hdr_folder && hdr_message && hdr_flags) {
2249 e_msg_composer_set_header (composer, "X-Evolution-Source-Folder", hdr_folder);
2250 e_msg_composer_set_header (composer, "X-Evolution-Source-Message", hdr_message);
2251 e_msg_composer_set_header (composer, "X-Evolution-Source-Flags", hdr_flags);
2252 }
2253 }
2254
2255 e_msg_composer_setup_with_message (composer, message, keep_signature, override_identity_uid, override_alias_name, override_alias_address, NULL);
2256
2257 g_free (override_identity_uid);
2258 g_free (override_alias_name);
2259 g_free (override_alias_address);
2260
2261 /* Override PostTo header only if the folder is a regular folder */
2262 if (folder && !folder_is_sent && !folder_is_drafts && !folder_is_outbox && !folder_is_templates) {
2263 EComposerHeaderTable *table;
2264 gchar *folder_uri;
2265 GList *list;
2266
2267 table = e_msg_composer_get_header_table (composer);
2268
2269 folder_uri = e_mail_folder_uri_from_folder (folder);
2270
2271 list = g_list_prepend (NULL, folder_uri);
2272 e_composer_header_table_set_post_to_list (table, list);
2273 g_list_free (list);
2274
2275 g_free (folder_uri);
2276 }
2277
2278 e_msg_composer_remove_header (
2279 composer, "X-Evolution-Replace-Outbox-UID");
2280
2281 if (message_uid != NULL && folder_is_drafts && folder) {
2282 gchar *folder_uri;
2283
2284 folder_uri = e_mail_folder_uri_from_folder (folder);
2285
2286 e_msg_composer_set_draft_headers (
2287 composer, folder_uri, message_uid);
2288
2289 g_free (folder_uri);
2290
2291 } else if (message_uid != NULL && folder_is_outbox) {
2292 CamelMessageInfo *info;
2293
2294 e_msg_composer_set_header (
2295 composer, "X-Evolution-Replace-Outbox-UID",
2296 message_uid);
2297
2298 info = camel_folder_get_message_info (folder, message_uid);
2299 if (info) {
2300 OutboxData *od;
2301
2302 /* This makes the message not to send it while it's being edited */
2303 g_object_set_data (G_OBJECT (info), MAIL_USER_KEY_EDITING, GINT_TO_POINTER (1));
2304
2305 od = g_new0 (OutboxData, 1);
2306 od->session = e_msg_composer_ref_session (composer);
2307 od->info = info; /* takes ownership of it */
2308
2309 g_object_set_data_full (G_OBJECT (composer), MAIL_USER_KEY_EDITING, od, outbox_data_free);
2310 }
2311 }
2312
2313 composer_set_no_change (composer);
2314
2315 gtk_widget_show (GTK_WIDGET (composer));
2316 }
2317
2318 static void
emu_update_composers_security(EMsgComposer * composer,guint32 validity_found)2319 emu_update_composers_security (EMsgComposer *composer,
2320 guint32 validity_found)
2321 {
2322 GtkAction *action;
2323 GSettings *settings;
2324 gboolean sign_reply;
2325
2326 g_return_if_fail (composer != NULL);
2327
2328 settings = e_util_ref_settings ("org.gnome.evolution.mail");
2329 sign_reply = (validity_found & E_MAIL_PART_VALIDITY_SIGNED) != 0 &&
2330 g_settings_get_boolean (settings, "composer-sign-reply-if-signed");
2331 g_object_unref (settings);
2332
2333 /* Pre-set only for encrypted messages, not for signed */
2334 if (sign_reply) {
2335 action = NULL;
2336
2337 if (validity_found & E_MAIL_PART_VALIDITY_SMIME) {
2338 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer))) &&
2339 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer))))
2340 action = E_COMPOSER_ACTION_SMIME_SIGN (composer);
2341 } else {
2342 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer))) &&
2343 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer))))
2344 action = E_COMPOSER_ACTION_PGP_SIGN (composer);
2345 }
2346
2347 if (action)
2348 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2349 }
2350
2351 if (validity_found & E_MAIL_PART_VALIDITY_ENCRYPTED) {
2352 action = NULL;
2353
2354 if (validity_found & E_MAIL_PART_VALIDITY_SMIME) {
2355 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer))) &&
2356 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer))))
2357 action = E_COMPOSER_ACTION_SMIME_ENCRYPT (composer);
2358 } else {
2359 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer))) &&
2360 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer))))
2361 action = E_COMPOSER_ACTION_PGP_ENCRYPT (composer);
2362 }
2363
2364 if (action)
2365 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2366 }
2367 }
2368
2369 void
em_utils_get_real_folder_uri_and_message_uid(CamelFolder * folder,const gchar * uid,gchar ** folder_uri,gchar ** message_uid)2370 em_utils_get_real_folder_uri_and_message_uid (CamelFolder *folder,
2371 const gchar *uid,
2372 gchar **folder_uri,
2373 gchar **message_uid)
2374 {
2375 g_return_if_fail (folder != NULL);
2376 g_return_if_fail (uid != NULL);
2377 g_return_if_fail (folder_uri != NULL);
2378 g_return_if_fail (message_uid != NULL);
2379
2380 em_utils_get_real_folder_and_message_uid (folder, uid, NULL, folder_uri, message_uid);
2381 }
2382
2383 static void
emu_add_composer_references_from_message(EMsgComposer * composer,CamelMimeMessage * message)2384 emu_add_composer_references_from_message (EMsgComposer *composer,
2385 CamelMimeMessage *message)
2386 {
2387 const gchar *message_id_header;
2388
2389 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2390 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2391
2392 message_id_header = camel_mime_message_get_message_id (message);
2393 if (message_id_header && *message_id_header) {
2394 GString *references = g_string_new ("");
2395 gint ii = 0;
2396 const gchar *value;
2397 gchar *unfolded;
2398
2399 while (value = e_msg_composer_get_header (composer, "References", ii), value) {
2400 ii++;
2401
2402 if (references->len)
2403 g_string_append_c (references, ' ');
2404 g_string_append (references, value);
2405 }
2406
2407 if (references->len)
2408 g_string_append_c (references, ' ');
2409
2410 if (*message_id_header != '<')
2411 g_string_append_c (references, '<');
2412
2413 g_string_append (references, message_id_header);
2414
2415 if (*message_id_header != '<')
2416 g_string_append_c (references, '>');
2417
2418 unfolded = camel_header_unfold (references->str);
2419
2420 e_msg_composer_set_header (composer, "References", unfolded);
2421
2422 g_string_free (references, TRUE);
2423 g_free (unfolded);
2424 }
2425 }
2426
2427 static void
real_update_forwarded_flag(gpointer uid,gpointer folder)2428 real_update_forwarded_flag (gpointer uid,
2429 gpointer folder)
2430 {
2431 if (uid && folder)
2432 camel_folder_set_message_flags (
2433 folder, uid, CAMEL_MESSAGE_FORWARDED,
2434 CAMEL_MESSAGE_FORWARDED);
2435 }
2436
2437 static void
update_forwarded_flags_cb(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,ForwardData * data)2438 update_forwarded_flags_cb (EMsgComposer *composer,
2439 CamelMimeMessage *message,
2440 EActivity *activity,
2441 ForwardData *data)
2442 {
2443 if (data && data->uids && data->folder)
2444 g_ptr_array_foreach (
2445 data->uids, real_update_forwarded_flag, data->folder);
2446 }
2447
2448 static void
emu_set_source_headers(EMsgComposer * composer,CamelFolder * folder,const gchar * message_uid,guint32 flags)2449 emu_set_source_headers (EMsgComposer *composer,
2450 CamelFolder *folder,
2451 const gchar *message_uid,
2452 guint32 flags)
2453 {
2454 gchar *source_folder_uri = NULL;
2455 gchar *source_message_uid = NULL;
2456
2457 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2458
2459 if (!folder || !message_uid)
2460 return;
2461 g_return_if_fail (CAMEL_IS_FOLDER (folder));
2462
2463 em_utils_get_real_folder_uri_and_message_uid (folder, message_uid,
2464 &source_folder_uri, &source_message_uid);
2465
2466 if (!source_message_uid)
2467 source_message_uid = g_strdup (message_uid);
2468
2469 if (source_folder_uri && source_message_uid)
2470 e_msg_composer_set_source_headers (composer, source_folder_uri, source_message_uid, flags);
2471
2472 g_free (source_folder_uri);
2473 g_free (source_message_uid);
2474 }
2475
2476 static void
setup_forward_attached_callbacks(EMsgComposer * composer,CamelFolder * folder,GPtrArray * uids)2477 setup_forward_attached_callbacks (EMsgComposer *composer,
2478 CamelFolder *folder,
2479 GPtrArray *uids)
2480 {
2481 ForwardData *data;
2482
2483 if (!composer || !folder || !uids || !uids->len)
2484 return;
2485
2486 if (uids->len == 1) {
2487 emu_set_source_headers (composer, folder, uids->pdata[0], CAMEL_MESSAGE_FORWARDED);
2488 return;
2489 }
2490
2491 data = g_slice_new0 (ForwardData);
2492 data->folder = g_object_ref (folder);
2493 data->uids = g_ptr_array_ref (uids);
2494
2495 g_signal_connect (
2496 composer, "send",
2497 G_CALLBACK (update_forwarded_flags_cb), data);
2498 g_signal_connect (
2499 composer, "save-to-drafts",
2500 G_CALLBACK (update_forwarded_flags_cb), data);
2501
2502 g_object_set_data_full (
2503 G_OBJECT (composer), "forward-data", data,
2504 (GDestroyNotify) forward_data_free);
2505 }
2506
2507 static void
forward_non_attached(EMsgComposer * composer,CamelFolder * folder,const gchar * uid,CamelMimeMessage * message,EMailForwardStyle style)2508 forward_non_attached (EMsgComposer *composer,
2509 CamelFolder *folder,
2510 const gchar *uid,
2511 CamelMimeMessage *message,
2512 EMailForwardStyle style)
2513 {
2514 CamelSession *session;
2515 EMailPartList *part_list = NULL;
2516 gchar *text, *forward, *subject;
2517 guint32 validity_found = 0;
2518 guint32 flags;
2519
2520 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2521
2522 session = e_msg_composer_ref_session (composer);
2523
2524 flags = E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS |
2525 E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG;
2526 if (style == E_MAIL_FORWARD_STYLE_QUOTED)
2527 flags |= E_MAIL_FORMATTER_QUOTE_FLAG_CITE;
2528 if (!e_content_editor_get_html_mode (e_html_editor_get_content_editor (e_msg_composer_get_editor (composer))))
2529 flags |= E_MAIL_FORMATTER_QUOTE_FLAG_NO_FORMATTING;
2530
2531 /* Setup composer's From account before calling quoting_text(),
2532 because quoting_text() relies on that account. */
2533 subject = mail_tool_generate_forward_subject (message);
2534 set_up_new_composer (composer, subject, folder, message, uid, FALSE);
2535 g_free (subject);
2536
2537 forward = quoting_text (QUOTING_FORWARD, composer);
2538 text = em_utils_message_to_html_ex (session, message, forward, flags, NULL, NULL, NULL, &validity_found, &part_list);
2539
2540 e_msg_composer_add_attachments_from_part_list (composer, part_list, FALSE);
2541
2542 if (text != NULL) {
2543 e_msg_composer_set_body_text (composer, text, TRUE);
2544
2545 emu_add_composer_references_from_message (composer, message);
2546
2547 emu_set_source_headers (composer, folder, uid, CAMEL_MESSAGE_FORWARDED);
2548
2549 emu_update_composers_security (composer, validity_found);
2550 composer_set_no_change (composer);
2551 gtk_widget_show (GTK_WIDGET (composer));
2552
2553 g_free (text);
2554 }
2555
2556 g_clear_object (&session);
2557 g_clear_object (&part_list);
2558 g_free (forward);
2559 }
2560
2561 /**
2562 * em_utils_forward_message:
2563 * @composer: an #EMsgComposer
2564 * @message: a #CamelMimeMessage to forward
2565 * @style: the forward style to use
2566 * @folder: (nullable): a #CamelFolder, or %NULL
2567 * @uid: (nullable): the UID of %message, or %NULL
2568 *
2569 * Forwards @message in the given @style.
2570 *
2571 * If @style is #E_MAIL_FORWARD_STYLE_ATTACHED, the new message is
2572 * created as follows. If there is more than a single message in @uids,
2573 * a multipart/digest will be constructed and attached to a new composer
2574 * window preset with the appropriate header defaults for forwarding the
2575 * first message in the list. If only one message is to be forwarded,
2576 * it is forwarded as a simple message/rfc822 attachment.
2577 *
2578 * If @style is #E_MAIL_FORWARD_STYLE_INLINE, each message is forwarded
2579 * in its own composer window in 'inline' form.
2580 *
2581 * If @style is #E_MAIL_FORWARD_STYLE_QUOTED, each message is forwarded
2582 * in its own composer window in 'quoted' form (each line starting with
2583 * a "> ").
2584 **/
2585 void
em_utils_forward_message(EMsgComposer * composer,CamelMimeMessage * message,EMailForwardStyle style,CamelFolder * folder,const gchar * uid)2586 em_utils_forward_message (EMsgComposer *composer,
2587 CamelMimeMessage *message,
2588 EMailForwardStyle style,
2589 CamelFolder *folder,
2590 const gchar *uid)
2591 {
2592 CamelMimePart *part;
2593 GPtrArray *uids = NULL;
2594 gchar *subject;
2595
2596 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2597 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2598
2599 e_msg_composer_set_is_reply_or_forward (composer, TRUE);
2600
2601 switch (style) {
2602 case E_MAIL_FORWARD_STYLE_ATTACHED:
2603 default:
2604 part = mail_tool_make_message_attachment (message);
2605 subject = mail_tool_generate_forward_subject (message);
2606
2607 if (folder && uid) {
2608 uids = g_ptr_array_new ();
2609 g_ptr_array_add (uids, (gpointer) uid);
2610 }
2611
2612 em_utils_forward_attachment (composer, part, subject, uids ? folder : NULL, uids);
2613
2614 g_object_unref (part);
2615 g_free (subject);
2616 break;
2617
2618 case E_MAIL_FORWARD_STYLE_INLINE:
2619 case E_MAIL_FORWARD_STYLE_QUOTED:
2620 forward_non_attached (composer, folder, uid, message, style);
2621 break;
2622 }
2623
2624 if (uids)
2625 g_ptr_array_unref (uids);
2626 }
2627
2628 void
em_utils_forward_attachment(EMsgComposer * composer,CamelMimePart * part,const gchar * subject,CamelFolder * folder,GPtrArray * uids)2629 em_utils_forward_attachment (EMsgComposer *composer,
2630 CamelMimePart *part,
2631 const gchar *subject,
2632 CamelFolder *folder,
2633 GPtrArray *uids)
2634 {
2635 CamelDataWrapper *content;
2636
2637 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2638 g_return_if_fail (CAMEL_IS_MIME_PART (part));
2639
2640 if (folder != NULL)
2641 g_return_if_fail (CAMEL_IS_FOLDER (folder));
2642
2643 e_msg_composer_set_is_reply_or_forward (composer, TRUE);
2644
2645 set_up_new_composer (composer, subject, folder, NULL, NULL, FALSE);
2646
2647 e_msg_composer_attach (composer, part);
2648
2649 content = camel_medium_get_content (CAMEL_MEDIUM (part));
2650 if (CAMEL_IS_MIME_MESSAGE (content)) {
2651 emu_add_composer_references_from_message (composer, CAMEL_MIME_MESSAGE (content));
2652 } else if (CAMEL_IS_MULTIPART (content)) {
2653 gchar *mime_type;
2654
2655 mime_type = camel_data_wrapper_get_mime_type (content);
2656 if (mime_type && g_ascii_strcasecmp (mime_type, "multipart/digest") == 0) {
2657 /* This is the way evolution forwards multiple messages as attachment */
2658 CamelMultipart *multipart;
2659 guint ii, nparts;
2660
2661 multipart = CAMEL_MULTIPART (content);
2662 nparts = camel_multipart_get_number (multipart);
2663
2664 for (ii = 0; ii < nparts; ii++) {
2665 CamelMimePart *mpart;
2666
2667 g_free (mime_type);
2668
2669 mpart = camel_multipart_get_part (multipart, ii);
2670 mime_type = camel_data_wrapper_get_mime_type (CAMEL_DATA_WRAPPER (mpart));
2671
2672 if (mime_type && g_ascii_strcasecmp (mime_type, "message/rfc822") == 0) {
2673 content = camel_medium_get_content (CAMEL_MEDIUM (mpart));
2674
2675 if (CAMEL_IS_MIME_MESSAGE (content))
2676 emu_add_composer_references_from_message (composer, CAMEL_MIME_MESSAGE (content));
2677 }
2678 }
2679 }
2680
2681 g_free (mime_type);
2682 }
2683
2684 if (uids != NULL)
2685 setup_forward_attached_callbacks (composer, folder, uids);
2686
2687 composer_set_no_change (composer);
2688
2689 gtk_widget_show (GTK_WIDGET (composer));
2690 }
2691
2692 /**
2693 * em_utils_redirect_message:
2694 * @composer: an #EMsgComposer
2695 * @message: message to redirect
2696 *
2697 * Sets up the @composer to redirect @message (Note: only headers will be
2698 * editable). Adds Resent-From/Resent-To/etc headers.
2699 *
2700 * Since: 3.22
2701 **/
2702 void
em_utils_redirect_message(EMsgComposer * composer,CamelMimeMessage * message)2703 em_utils_redirect_message (EMsgComposer *composer,
2704 CamelMimeMessage *message)
2705 {
2706 ESource *source;
2707 EShell *shell;
2708 CamelMedium *medium;
2709 gchar *identity_uid = NULL, *alias_name = NULL, *alias_address = NULL;
2710
2711 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2712 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2713
2714 shell = e_msg_composer_get_shell (composer);
2715 medium = CAMEL_MEDIUM (message);
2716
2717 /* QMail will refuse to send a message if it finds one of
2718 * it's Delivered-To headers in the message, so remove all
2719 * Delivered-To headers. Fixes bug #23635. */
2720 while (camel_medium_get_header (medium, "Delivered-To"))
2721 camel_medium_remove_header (medium, "Delivered-To");
2722
2723 while (camel_medium_get_header (medium, "Bcc"))
2724 camel_medium_remove_header (medium, "Bcc");
2725
2726 while (camel_medium_get_header (medium, "Resent-Bcc"))
2727 camel_medium_remove_header (medium, "Resent-Bcc");
2728
2729 source = em_composer_utils_guess_identity_source (shell, message, NULL, NULL, &alias_name, &alias_address);
2730
2731 if (source != NULL) {
2732 identity_uid = e_source_dup_uid (source);
2733 g_object_unref (source);
2734 }
2735
2736 e_msg_composer_setup_redirect (composer, message, identity_uid, alias_name, alias_address, NULL);
2737
2738 g_free (identity_uid);
2739 g_free (alias_name);
2740 g_free (alias_address);
2741
2742 gtk_widget_show (GTK_WIDGET (composer));
2743
2744 composer_set_no_change (composer);
2745 }
2746
2747 /* Replying to messages... */
2748
2749 EDestination **
em_utils_camel_address_to_destination(CamelInternetAddress * iaddr)2750 em_utils_camel_address_to_destination (CamelInternetAddress *iaddr)
2751 {
2752 EDestination *dest, **destv;
2753 gint n, i, j;
2754
2755 if (iaddr == NULL)
2756 return NULL;
2757
2758 if ((n = camel_address_length ((CamelAddress *) iaddr)) == 0)
2759 return NULL;
2760
2761 destv = g_malloc (sizeof (EDestination *) * (n + 1));
2762 for (i = 0, j = 0; i < n; i++) {
2763 const gchar *name, *addr;
2764
2765 if (camel_internet_address_get (iaddr, i, &name, &addr)) {
2766 dest = e_destination_new ();
2767 e_destination_set_name (dest, name);
2768 e_destination_set_email (dest, addr);
2769
2770 destv[j++] = dest;
2771 }
2772 }
2773
2774 if (j == 0) {
2775 g_free (destv);
2776 return NULL;
2777 }
2778
2779 destv[j] = NULL;
2780
2781 return destv;
2782 }
2783
2784 static gchar *
emcu_construct_reply_subject(const gchar * source_subject)2785 emcu_construct_reply_subject (const gchar *source_subject)
2786 {
2787 gchar *res;
2788
2789 if (source_subject) {
2790 GSettings *settings;
2791 gboolean skip_len = -1;
2792
2793 if (em_utils_is_re_in_subject (source_subject, &skip_len, NULL, NULL) && skip_len > 0)
2794 source_subject = source_subject + skip_len;
2795
2796 settings = e_util_ref_settings ("org.gnome.evolution.mail");
2797 if (g_settings_get_boolean (settings, "composer-use-localized-fwd-re")) {
2798 /* Translators: This is a reply attribution in the message reply subject. The %s is replaced with the subject of the original message. Both 'Re'-s in the 'reply-attribution' translation context should translate into the same string, the same as the ':' separator. */
2799 res = g_strdup_printf (C_("reply-attribution", "Re: %s"), source_subject);
2800 } else {
2801 /* Do not localize this string */
2802 res = g_strdup_printf ("Re: %s", source_subject);
2803 }
2804 g_clear_object (&settings);
2805 } else {
2806 res = g_strdup ("");
2807 }
2808
2809 return res;
2810 }
2811
2812 static void
reply_setup_composer_recipients(EMsgComposer * composer,CamelInternetAddress * to,CamelInternetAddress * cc,CamelFolder * folder,const gchar * message_uid,CamelNNTPAddress * postto)2813 reply_setup_composer_recipients (EMsgComposer *composer,
2814 CamelInternetAddress *to,
2815 CamelInternetAddress *cc,
2816 CamelFolder *folder,
2817 const gchar *message_uid,
2818 CamelNNTPAddress *postto)
2819 {
2820 EComposerHeaderTable *table;
2821 EDestination **tov, **ccv;
2822
2823 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2824 if (to != NULL)
2825 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to));
2826
2827 if (cc != NULL)
2828 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc));
2829
2830 /* Construct the tov/ccv */
2831 tov = em_utils_camel_address_to_destination (to);
2832 ccv = em_utils_camel_address_to_destination (cc);
2833
2834 table = e_msg_composer_get_header_table (composer);
2835 e_composer_header_table_set_destinations_to (table, tov);
2836
2837 /* Add destinations instead of setting, so we don't remove
2838 * automatic CC addresses that have already been added. */
2839 e_composer_header_table_add_destinations_cc (table, ccv);
2840
2841 e_destination_freev (tov);
2842 e_destination_freev (ccv);
2843
2844 /* Add post-to, if necessary */
2845 if (postto && camel_address_length ((CamelAddress *) postto)) {
2846 CamelFolder *use_folder = folder, *temp_folder = NULL;
2847 gchar *store_url = NULL;
2848 gchar *post;
2849
2850 if (use_folder && CAMEL_IS_VEE_FOLDER (use_folder) && message_uid) {
2851 em_utils_get_real_folder_and_message_uid (use_folder, message_uid, &temp_folder, NULL, NULL);
2852
2853 if (temp_folder)
2854 use_folder = temp_folder;
2855 }
2856
2857 if (use_folder) {
2858 CamelStore *parent_store;
2859 CamelService *service;
2860 CamelURL *url;
2861
2862 parent_store = camel_folder_get_parent_store (use_folder);
2863
2864 service = CAMEL_SERVICE (parent_store);
2865 url = camel_service_new_camel_url (service);
2866
2867 store_url = camel_url_to_string (
2868 url, CAMEL_URL_HIDE_ALL);
2869 if (store_url[strlen (store_url) - 1] == '/')
2870 store_url[strlen (store_url) - 1] = '\0';
2871
2872 camel_url_free (url);
2873 }
2874
2875 post = camel_address_encode ((CamelAddress *) postto);
2876 e_composer_header_table_set_post_to_base (
2877 table, store_url ? store_url : "", post);
2878 g_free (post);
2879 g_free (store_url);
2880 g_clear_object (&temp_folder);
2881 }
2882 }
2883
2884 static void
reply_setup_composer(EMsgComposer * composer,CamelMimeMessage * message,const gchar * identity_uid,const gchar * identity_name,const gchar * identity_address,CamelInternetAddress * to,CamelInternetAddress * cc,CamelFolder * folder,const gchar * message_uid,CamelNNTPAddress * postto)2885 reply_setup_composer (EMsgComposer *composer,
2886 CamelMimeMessage *message,
2887 const gchar *identity_uid,
2888 const gchar *identity_name,
2889 const gchar *identity_address,
2890 CamelInternetAddress *to,
2891 CamelInternetAddress *cc,
2892 CamelFolder *folder,
2893 const gchar *message_uid,
2894 CamelNNTPAddress *postto)
2895 {
2896 gchar *message_id, *references;
2897 EComposerHeaderTable *table;
2898 CamelMedium *medium;
2899 gchar *subject;
2900
2901 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2902 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2903
2904 e_msg_composer_set_is_reply_or_forward (composer, TRUE);
2905
2906 if (to != NULL)
2907 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to));
2908
2909 if (cc != NULL)
2910 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc));
2911
2912 reply_setup_composer_recipients (composer, to, cc, folder, message_uid, postto);
2913
2914 /* Set the subject of the new message. */
2915 subject = emcu_construct_reply_subject (camel_mime_message_get_subject (message));
2916
2917 table = e_msg_composer_get_header_table (composer);
2918 e_composer_header_table_set_subject (table, subject);
2919 e_composer_header_table_set_identity_uid (table, identity_uid, identity_name, identity_address);
2920
2921 g_free (subject);
2922
2923 /* Add In-Reply-To and References. */
2924
2925 medium = CAMEL_MEDIUM (message);
2926 message_id = camel_header_unfold (camel_medium_get_header (medium, "Message-ID"));
2927 references = camel_header_unfold (camel_medium_get_header (medium, "References"));
2928
2929 if (message_id != NULL) {
2930 gchar *reply_refs;
2931
2932 e_msg_composer_add_header (
2933 composer, "In-Reply-To", message_id);
2934
2935 if (references)
2936 reply_refs = g_strdup_printf (
2937 "%s %s", references, message_id);
2938 else
2939 reply_refs = g_strdup (message_id);
2940
2941 e_msg_composer_add_header (
2942 composer, "References", reply_refs);
2943 g_free (reply_refs);
2944
2945 } else if (references != NULL) {
2946 e_msg_composer_add_header (
2947 composer, "References", references);
2948 }
2949
2950 g_free (message_id);
2951 g_free (references);
2952 }
2953
2954 static gboolean
get_reply_list(CamelMimeMessage * message,CamelInternetAddress * to)2955 get_reply_list (CamelMimeMessage *message,
2956 CamelInternetAddress *to)
2957 {
2958 const gchar *header, *p;
2959 gchar *addr;
2960
2961 /* Examples:
2962 *
2963 * List-Post: <mailto:list@host.com>
2964 * List-Post: <mailto:moderator@host.com?subject=list%20posting>
2965 * List-Post: NO (posting not allowed on this list)
2966 */
2967 if (!(header = camel_medium_get_header ((CamelMedium *) message, "List-Post")))
2968 return FALSE;
2969
2970 while (*header == ' ' || *header == '\t')
2971 header++;
2972
2973 /* check for NO */
2974 if (!g_ascii_strncasecmp (header, "NO", 2))
2975 return FALSE;
2976
2977 /* Search for the first mailto angle-bracket enclosed URL.
2978 * (See rfc2369, Section 2, paragraph 3 for details) */
2979 if (!(header = camel_strstrcase (header, "<mailto:")))
2980 return FALSE;
2981
2982 header += 8;
2983
2984 p = header;
2985 while (*p && !strchr ("?>", *p))
2986 p++;
2987
2988 addr = g_strndup (header, p - header);
2989 camel_internet_address_add (to, NULL, addr);
2990 g_free (addr);
2991
2992 return TRUE;
2993 }
2994
2995 gboolean
em_utils_is_munged_list_message(CamelMimeMessage * message)2996 em_utils_is_munged_list_message (CamelMimeMessage *message)
2997 {
2998 CamelInternetAddress *reply_to, *list;
2999 gboolean result = FALSE;
3000
3001 reply_to = camel_mime_message_get_reply_to (message);
3002 if (reply_to) {
3003 list = camel_internet_address_new ();
3004
3005 if (get_reply_list (message, list) &&
3006 camel_address_length (CAMEL_ADDRESS (list)) ==
3007 camel_address_length (CAMEL_ADDRESS (reply_to))) {
3008 gint i;
3009 const gchar *r_name, *r_addr;
3010 const gchar *l_name, *l_addr;
3011
3012 for (i = 0; i < camel_address_length (CAMEL_ADDRESS (list)); i++) {
3013 if (!camel_internet_address_get (reply_to, i, &r_name, &r_addr))
3014 break;
3015 if (!camel_internet_address_get (list, i, &l_name, &l_addr))
3016 break;
3017 if (strcmp (l_addr, r_addr))
3018 break;
3019 }
3020 if (i == camel_address_length (CAMEL_ADDRESS (list)))
3021 result = TRUE;
3022 }
3023 g_object_unref (list);
3024 }
3025 return result;
3026 }
3027
3028 static CamelInternetAddress *
get_reply_to(CamelMimeMessage * message)3029 get_reply_to (CamelMimeMessage *message)
3030 {
3031 CamelInternetAddress *reply_to;
3032
3033 reply_to = camel_mime_message_get_reply_to (message);
3034 if (reply_to) {
3035 GSettings *settings;
3036 gboolean ignore_list_reply_to;
3037
3038 settings = e_util_ref_settings ("org.gnome.evolution.mail");
3039 ignore_list_reply_to = g_settings_get_boolean (
3040 settings, "composer-ignore-list-reply-to");
3041 g_object_unref (settings);
3042
3043 if (ignore_list_reply_to && em_utils_is_munged_list_message (message))
3044 reply_to = NULL;
3045 }
3046 if (!reply_to)
3047 reply_to = camel_mime_message_get_from (message);
3048
3049 return reply_to;
3050 }
3051
3052 static void
get_reply_sender(CamelMimeMessage * message,CamelInternetAddress * to,CamelNNTPAddress * postto)3053 get_reply_sender (CamelMimeMessage *message,
3054 CamelInternetAddress *to,
3055 CamelNNTPAddress *postto)
3056 {
3057 CamelInternetAddress *reply_to;
3058 CamelMedium *medium;
3059 const gchar *posthdr = NULL;
3060
3061 medium = CAMEL_MEDIUM (message);
3062
3063 /* check whether there is a 'Newsgroups: ' header in there */
3064 if (postto != NULL && posthdr == NULL)
3065 posthdr = camel_medium_get_header (medium, "Followup-To");
3066
3067 if (postto != NULL && posthdr == NULL)
3068 posthdr = camel_medium_get_header (medium, "Newsgroups");
3069
3070 if (postto != NULL && posthdr != NULL) {
3071 camel_address_decode (CAMEL_ADDRESS (postto), posthdr);
3072 return;
3073 }
3074
3075 reply_to = get_reply_to (message);
3076
3077 if (reply_to != NULL) {
3078 const gchar *name;
3079 const gchar *addr;
3080 gint ii = 0;
3081
3082 while (camel_internet_address_get (reply_to, ii++, &name, &addr))
3083 camel_internet_address_add (to, name, addr);
3084 }
3085 }
3086
3087 void
em_utils_get_reply_sender(CamelMimeMessage * message,CamelInternetAddress * to,CamelNNTPAddress * postto)3088 em_utils_get_reply_sender (CamelMimeMessage *message,
3089 CamelInternetAddress *to,
3090 CamelNNTPAddress *postto)
3091 {
3092 get_reply_sender (message, to, postto);
3093 }
3094
3095 static void
get_reply_from(CamelMimeMessage * message,CamelInternetAddress * to,CamelNNTPAddress * postto)3096 get_reply_from (CamelMimeMessage *message,
3097 CamelInternetAddress *to,
3098 CamelNNTPAddress *postto)
3099 {
3100 CamelInternetAddress *from;
3101 CamelMedium *medium;
3102 const gchar *name, *addr;
3103 const gchar *posthdr = NULL;
3104
3105 medium = CAMEL_MEDIUM (message);
3106
3107 /* check whether there is a 'Newsgroups: ' header in there */
3108 if (postto != NULL && posthdr == NULL)
3109 posthdr = camel_medium_get_header (medium, "Followup-To");
3110
3111 if (postto != NULL && posthdr == NULL)
3112 posthdr = camel_medium_get_header (medium, "Newsgroups");
3113
3114 if (postto != NULL && posthdr != NULL) {
3115 camel_address_decode (CAMEL_ADDRESS (postto), posthdr);
3116 return;
3117 }
3118
3119 from = camel_mime_message_get_from (message);
3120
3121 if (from != NULL) {
3122 gint ii = 0;
3123
3124 while (camel_internet_address_get (from, ii++, &name, &addr))
3125 camel_internet_address_add (to, name, addr);
3126 }
3127 }
3128
3129 static void
get_reply_recipient(CamelMimeMessage * message,CamelInternetAddress * to,CamelNNTPAddress * postto,CamelInternetAddress * address)3130 get_reply_recipient (CamelMimeMessage *message,
3131 CamelInternetAddress *to,
3132 CamelNNTPAddress *postto,
3133 CamelInternetAddress *address)
3134 {
3135 CamelMedium *medium;
3136 const gchar *posthdr = NULL;
3137
3138 medium = CAMEL_MEDIUM (message);
3139
3140 /* check whether there is a 'Newsgroups: ' header in there */
3141 if (postto != NULL && posthdr == NULL)
3142 posthdr = camel_medium_get_header (medium, "Followup-To");
3143
3144 if (postto != NULL && posthdr == NULL)
3145 posthdr = camel_medium_get_header (medium, "Newsgroups");
3146
3147 if (postto != NULL && posthdr != NULL) {
3148 camel_address_decode (CAMEL_ADDRESS (postto), posthdr);
3149 return;
3150 }
3151
3152 if (address != NULL) {
3153 const gchar *name;
3154 const gchar *addr;
3155 gint ii = 0;
3156
3157 while (camel_internet_address_get (address, ii++, &name, &addr))
3158 camel_internet_address_add (to, name, addr);
3159 }
3160
3161 }
3162
3163 static void
concat_unique_addrs(CamelInternetAddress * dest,CamelInternetAddress * src,GHashTable * rcpt_hash)3164 concat_unique_addrs (CamelInternetAddress *dest,
3165 CamelInternetAddress *src,
3166 GHashTable *rcpt_hash)
3167 {
3168 const gchar *name, *addr;
3169 gint i;
3170
3171 for (i = 0; camel_internet_address_get (src, i, &name, &addr); i++) {
3172 if (!g_hash_table_contains (rcpt_hash, addr)) {
3173 camel_internet_address_add (dest, name, addr);
3174 g_hash_table_insert (rcpt_hash, g_strdup (addr), NULL);
3175 }
3176 }
3177 }
3178
3179 static void
add_source_to_recipient_hash(ESourceRegistry * registry,GHashTable * rcpt_hash,const gchar * address,ESource * source,gboolean source_is_default)3180 add_source_to_recipient_hash (ESourceRegistry *registry,
3181 GHashTable *rcpt_hash,
3182 const gchar *address,
3183 ESource *source,
3184 gboolean source_is_default)
3185 {
3186 ESource *cached_source;
3187 gboolean insert_source;
3188
3189 g_return_if_fail (rcpt_hash != NULL);
3190 g_return_if_fail (E_IS_SOURCE (source));
3191
3192 if (!address || !*address)
3193 return;
3194
3195 cached_source = g_hash_table_lookup (rcpt_hash, address);
3196
3197 insert_source = source_is_default || !cached_source;
3198
3199 if (insert_source)
3200 g_hash_table_insert (rcpt_hash, g_strdup (address), g_object_ref (source));
3201 }
3202
3203 static void
unref_nonull_object(gpointer ptr)3204 unref_nonull_object (gpointer ptr)
3205 {
3206 if (ptr)
3207 g_object_unref (ptr);
3208 }
3209
3210 static GHashTable *
generate_recipient_hash(ESourceRegistry * registry)3211 generate_recipient_hash (ESourceRegistry *registry)
3212 {
3213 GHashTable *rcpt_hash;
3214 ESource *default_source;
3215 GList *list, *link;
3216 const gchar *extension_name;
3217
3218 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
3219
3220 rcpt_hash = g_hash_table_new_full (
3221 camel_strcase_hash,
3222 camel_strcase_equal,
3223 g_free, unref_nonull_object);
3224
3225 default_source = e_source_registry_ref_default_mail_identity (registry);
3226
3227 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3228 list = e_source_registry_list_sources (registry, extension_name);
3229
3230 for (link = list; link != NULL; link = g_list_next (link)) {
3231 ESource *source = E_SOURCE (link->data);
3232 ESourceMailIdentity *extension;
3233 GHashTable *aliases;
3234 const gchar *address;
3235 gboolean source_is_default;
3236
3237 /* No default mail identity implies there are no mail
3238 * identities at all and so we should never get here. */
3239 g_warn_if_fail (default_source != NULL);
3240
3241 if (!e_source_registry_check_enabled (registry, source))
3242 continue;
3243
3244 source_is_default = e_source_equal (source, default_source);
3245
3246 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3247 extension = e_source_get_extension (source, extension_name);
3248
3249 address = e_source_mail_identity_get_address (extension);
3250
3251 add_source_to_recipient_hash (registry, rcpt_hash, address, source, source_is_default);
3252
3253 aliases = e_source_mail_identity_get_aliases_as_hash_table (extension);
3254 if (aliases) {
3255 GHashTableIter iter;
3256 gpointer key;
3257
3258 g_hash_table_iter_init (&iter, aliases);
3259 while (g_hash_table_iter_next (&iter, &key, NULL)) {
3260 address = key;
3261
3262 add_source_to_recipient_hash (registry, rcpt_hash, address, source, source_is_default);
3263 }
3264
3265 g_hash_table_destroy (aliases);
3266 }
3267 }
3268
3269 g_list_free_full (list, (GDestroyNotify) g_object_unref);
3270
3271 if (default_source != NULL)
3272 g_object_unref (default_source);
3273
3274 return rcpt_hash;
3275 }
3276
3277 void
em_utils_get_reply_all(ESourceRegistry * registry,CamelMimeMessage * message,CamelInternetAddress * to,CamelInternetAddress * cc,CamelNNTPAddress * postto)3278 em_utils_get_reply_all (ESourceRegistry *registry,
3279 CamelMimeMessage *message,
3280 CamelInternetAddress *to,
3281 CamelInternetAddress *cc,
3282 CamelNNTPAddress *postto)
3283 {
3284 CamelInternetAddress *reply_to;
3285 CamelInternetAddress *to_addrs;
3286 CamelInternetAddress *cc_addrs;
3287 CamelMedium *medium;
3288 const gchar *name, *addr;
3289 const gchar *posthdr = NULL;
3290 GHashTable *rcpt_hash;
3291
3292 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
3293 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
3294 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to));
3295 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc));
3296
3297 medium = CAMEL_MEDIUM (message);
3298
3299 /* check whether there is a 'Newsgroups: ' header in there */
3300 if (postto != NULL && posthdr == NULL)
3301 posthdr = camel_medium_get_header (medium, "Followup-To");
3302
3303 if (postto != NULL && posthdr == NULL)
3304 posthdr = camel_medium_get_header (medium, "Newsgroups");
3305
3306 if (postto != NULL && posthdr != NULL)
3307 camel_address_decode (CAMEL_ADDRESS (postto), posthdr);
3308
3309 rcpt_hash = generate_recipient_hash (registry);
3310
3311 reply_to = get_reply_to (message);
3312 to_addrs = camel_mime_message_get_recipients (
3313 message, CAMEL_RECIPIENT_TYPE_TO);
3314 cc_addrs = camel_mime_message_get_recipients (
3315 message, CAMEL_RECIPIENT_TYPE_CC);
3316
3317 if (reply_to != NULL) {
3318 gint ii = 0;
3319
3320 while (camel_internet_address_get (reply_to, ii++, &name, &addr)) {
3321 /* Ignore references to the Reply-To address
3322 * in the To and Cc lists. */
3323 if (addr && !g_hash_table_contains (rcpt_hash, addr)) {
3324 /* In the case we are doing a Reply-To-All,
3325 * we do not want to include the user's email
3326 * address because replying to oneself is
3327 * kinda silly. */
3328 camel_internet_address_add (to, name, addr);
3329 g_hash_table_insert (rcpt_hash, g_strdup (addr), NULL);
3330 }
3331 }
3332 }
3333
3334 concat_unique_addrs (to, to_addrs, rcpt_hash);
3335 concat_unique_addrs (cc, cc_addrs, rcpt_hash);
3336
3337 /* Set as the 'To' the first 'Reply-To' address, if such exists, when no address
3338 had been picked (like when all addresses are configured mail accounts). */
3339 if (reply_to &&
3340 camel_address_length ((CamelAddress *) to) == 0 &&
3341 camel_internet_address_get (reply_to, 0, &name, &addr)) {
3342 camel_internet_address_add (to, name, addr);
3343 }
3344
3345 /* Promote the first Cc: address to To: if To: is empty. */
3346 if (camel_address_length ((CamelAddress *) to) == 0 &&
3347 camel_address_length ((CamelAddress *) cc) > 0) {
3348 if (camel_internet_address_get (cc, 0, &name, &addr))
3349 camel_internet_address_add (to, name, addr);
3350 camel_address_remove ((CamelAddress *) cc, 0);
3351 }
3352
3353 /* If To: is still empty, may we removed duplicates (i.e. ourself),
3354 * so add the original To if it was set. */
3355 if (camel_address_length ((CamelAddress *) to) == 0
3356 && (camel_internet_address_get (to_addrs, 0, &name, &addr)
3357 || camel_internet_address_get (cc_addrs, 0, &name, &addr))) {
3358 camel_internet_address_add (to, name, addr);
3359 }
3360
3361 g_hash_table_destroy (rcpt_hash);
3362 }
3363
3364 enum {
3365 ATTRIB_UNKNOWN,
3366 ATTRIB_CUSTOM,
3367 ATTRIB_TIMEZONE,
3368 ATTRIB_STRFTIME,
3369 ATTRIB_TM_SEC,
3370 ATTRIB_TM_MIN,
3371 ATTRIB_TM_24HOUR,
3372 ATTRIB_TM_12HOUR,
3373 ATTRIB_TM_MDAY,
3374 ATTRIB_TM_MON,
3375 ATTRIB_TM_YEAR,
3376 ATTRIB_TM_2YEAR,
3377 ATTRIB_TM_WDAY, /* not actually used */
3378 ATTRIB_TM_YDAY
3379 };
3380
3381 typedef void (*AttribFormatter) (GString *str,
3382 const gchar *attr,
3383 CamelMimeMessage *message);
3384
3385 static void
format_sender(GString * str,const gchar * attr,CamelMimeMessage * message)3386 format_sender (GString *str,
3387 const gchar *attr,
3388 CamelMimeMessage *message)
3389 {
3390 CamelInternetAddress *sender;
3391 const gchar *name, *addr = NULL;
3392 gchar *tmp = NULL;
3393
3394 sender = camel_mime_message_get_from (message);
3395 if (sender != NULL && camel_address_length (CAMEL_ADDRESS (sender)) > 0) {
3396 name = NULL;
3397
3398 if (camel_internet_address_get (sender, 0, &name, &addr)) {
3399 if (name && !*name) {
3400 name = NULL;
3401 } else if (name && *name == '\"') {
3402 gint len = strlen (name);
3403
3404 if (len == 1) {
3405 name = NULL;
3406 } else if (len > 1 && name[len - 1] == '\"') {
3407 if (len == 2) {
3408 name = NULL;
3409 } else {
3410 tmp = g_strndup (name + 1, len - 2);
3411 name = tmp;
3412 }
3413 }
3414 }
3415 }
3416 } else {
3417 name = _("an unknown sender");
3418 }
3419
3420 if (name && !strcmp (attr, "{SenderName}")) {
3421 g_string_append (str, name);
3422 } else if (addr && !strcmp (attr, "{SenderEMail}")) {
3423 g_string_append (str, addr);
3424 } else if (name && *name) {
3425 g_string_append (str, name);
3426 } else if (addr) {
3427 g_string_append (str, addr);
3428 }
3429
3430 g_free (tmp);
3431 }
3432
3433 static struct {
3434 const gchar *name;
3435 gint type;
3436 struct {
3437 const gchar *format; /* strftime or printf format */
3438 AttribFormatter formatter; /* custom formatter */
3439 } v;
3440 } attribvars[] = {
3441 { "{Sender}", ATTRIB_CUSTOM, { NULL, format_sender } },
3442 { "{SenderName}", ATTRIB_CUSTOM, { NULL, format_sender } },
3443 { "{SenderEMail}", ATTRIB_CUSTOM, { NULL, format_sender } },
3444 { "{AbbrevWeekdayName}", ATTRIB_STRFTIME, { "%a", NULL } },
3445 { "{WeekdayName}", ATTRIB_STRFTIME, { "%A", NULL } },
3446 { "{AbbrevMonthName}", ATTRIB_STRFTIME, { "%b", NULL } },
3447 { "{MonthName}", ATTRIB_STRFTIME, { "%B", NULL } },
3448 { "{AmPmUpper}", ATTRIB_STRFTIME, { "%p", NULL } },
3449 { "{AmPmLower}", ATTRIB_STRFTIME, { "%P", NULL } },
3450 { "{Day}", ATTRIB_TM_MDAY, { "%02d", NULL } }, /* %d 01-31 */
3451 { "{ Day}", ATTRIB_TM_MDAY, { "% 2d", NULL } }, /* %e 1-31 */
3452 { "{24Hour}", ATTRIB_TM_24HOUR, { "%02d", NULL } }, /* %H 00-23 */
3453 { "{12Hour}", ATTRIB_TM_12HOUR, { "%02d", NULL } }, /* %I 00-12 */
3454 { "{DayOfYear}", ATTRIB_TM_YDAY, { "%d", NULL } }, /* %j 1-366 */
3455 { "{Month}", ATTRIB_TM_MON, { "%02d", NULL } }, /* %m 01-12 */
3456 { "{Minute}", ATTRIB_TM_MIN, { "%02d", NULL } }, /* %M 00-59 */
3457 { "{Seconds}", ATTRIB_TM_SEC, { "%02d", NULL } }, /* %S 00-61 */
3458 { "{2DigitYear}", ATTRIB_TM_2YEAR, { "%02d", NULL } }, /* %y */
3459 { "{Year}", ATTRIB_TM_YEAR, { "%04d", NULL } }, /* %Y */
3460 { "{TimeZone}", ATTRIB_TIMEZONE, { "%+05d", NULL } }
3461 };
3462
3463 gchar *
em_composer_utils_get_reply_credits(ESource * identity_source,CamelMimeMessage * message)3464 em_composer_utils_get_reply_credits (ESource *identity_source,
3465 CamelMimeMessage *message)
3466 {
3467 register const gchar *inptr;
3468 const gchar *start;
3469 gint tzone, len, i;
3470 gchar buf[64];
3471 GString *str;
3472 struct tm tm;
3473 time_t date;
3474 gint type;
3475 gchar *format, *restore_lc_messages = NULL, *restore_lc_time = NULL;
3476
3477 emcu_prepare_attribution_locale (identity_source, &restore_lc_messages, &restore_lc_time);
3478
3479 format = quoting_text (QUOTING_ATTRIBUTION, NULL);
3480 str = g_string_new ("");
3481
3482 date = camel_mime_message_get_date (message, &tzone);
3483
3484 if (date == CAMEL_MESSAGE_DATE_CURRENT) {
3485 /* The message has no Date: header, look at Received: */
3486 date = camel_mime_message_get_date_received (message, &tzone);
3487 }
3488 if (date == CAMEL_MESSAGE_DATE_CURRENT) {
3489 /* That didn't work either, use current time */
3490 time (&date);
3491 tzone = 0;
3492 } else if (tzone == 0) {
3493 GSettings *settings;
3494
3495 settings = e_util_ref_settings ("org.gnome.evolution.mail");
3496
3497 if (g_settings_get_boolean (settings, "composer-reply-credits-utc-to-localtime")) {
3498 struct tm local;
3499 gint offset = 0;
3500
3501 e_localtime_with_offset (date, &local, &offset);
3502
3503 tzone = offset / 3600;
3504 tzone = (tzone * 100) + ((offset / 60) % 60);
3505 }
3506
3507 g_clear_object (&settings);
3508 }
3509
3510 /* Convert to UTC */
3511 date += (tzone / 100) * 60 * 60;
3512 date += (tzone % 100) * 60;
3513
3514 gmtime_r (&date, &tm);
3515
3516 inptr = format;
3517 while (*inptr != '\0') {
3518 start = inptr;
3519 while (*inptr && strncmp (inptr, "${", 2) != 0)
3520 inptr++;
3521
3522 g_string_append_len (str, start, inptr - start);
3523
3524 if (*inptr == '\0')
3525 break;
3526
3527 start = ++inptr;
3528 while (*inptr && *inptr != '}')
3529 inptr++;
3530
3531 if (*inptr != '}') {
3532 /* broken translation */
3533 g_string_append_len (str, "${", 2);
3534 inptr = start + 1;
3535 continue;
3536 }
3537
3538 inptr++;
3539 len = inptr - start;
3540 type = ATTRIB_UNKNOWN;
3541 for (i = 0; i < G_N_ELEMENTS (attribvars); i++) {
3542 if (!strncmp (attribvars[i].name, start, len)) {
3543 type = attribvars[i].type;
3544 break;
3545 }
3546 }
3547
3548 switch (type) {
3549 case ATTRIB_CUSTOM:
3550 attribvars[i].v.formatter (
3551 str, attribvars[i].name, message);
3552 break;
3553 case ATTRIB_TIMEZONE:
3554 g_string_append_printf (
3555 str, attribvars[i].v.format, tzone);
3556 break;
3557 case ATTRIB_STRFTIME:
3558 e_utf8_strftime_match_lc_messages (
3559 buf, sizeof (buf), attribvars[i].v.format, &tm);
3560 g_string_append (str, buf);
3561 break;
3562 case ATTRIB_TM_SEC:
3563 g_string_append_printf (
3564 str, attribvars[i].v.format, tm.tm_sec);
3565 break;
3566 case ATTRIB_TM_MIN:
3567 g_string_append_printf (
3568 str, attribvars[i].v.format, tm.tm_min);
3569 break;
3570 case ATTRIB_TM_24HOUR:
3571 g_string_append_printf (
3572 str, attribvars[i].v.format, tm.tm_hour);
3573 break;
3574 case ATTRIB_TM_12HOUR:
3575 g_string_append_printf (
3576 str, attribvars[i].v.format,
3577 (tm.tm_hour + 1) % 13);
3578 break;
3579 case ATTRIB_TM_MDAY:
3580 g_string_append_printf (
3581 str, attribvars[i].v.format, tm.tm_mday);
3582 break;
3583 case ATTRIB_TM_MON:
3584 g_string_append_printf (
3585 str, attribvars[i].v.format, tm.tm_mon + 1);
3586 break;
3587 case ATTRIB_TM_YEAR:
3588 g_string_append_printf (
3589 str, attribvars[i].v.format, tm.tm_year + 1900);
3590 break;
3591 case ATTRIB_TM_2YEAR:
3592 g_string_append_printf (
3593 str, attribvars[i].v.format, tm.tm_year % 100);
3594 break;
3595 case ATTRIB_TM_WDAY:
3596 /* not actually used */
3597 g_string_append_printf (
3598 str, attribvars[i].v.format, tm.tm_wday);
3599 break;
3600 case ATTRIB_TM_YDAY:
3601 g_string_append_printf (
3602 str, attribvars[i].v.format, tm.tm_yday + 1);
3603 break;
3604 default:
3605 /* Misspelled variable? Drop the
3606 * format argument and continue. */
3607 break;
3608 }
3609 }
3610
3611 emcu_restore_locale_after_attribution (restore_lc_messages, restore_lc_time);
3612
3613 g_free (format);
3614
3615 return g_string_free (str, FALSE);
3616 }
3617
3618 static void
composer_set_body(EMsgComposer * composer,CamelMimeMessage * message,EMailReplyStyle style,EMailPartList * parts_list,EMailPartList ** out_used_part_list)3619 composer_set_body (EMsgComposer *composer,
3620 CamelMimeMessage *message,
3621 EMailReplyStyle style,
3622 EMailPartList *parts_list,
3623 EMailPartList **out_used_part_list)
3624 {
3625 gchar *text, *credits, *original;
3626 ESource *identity_source;
3627 CamelMimePart *part;
3628 CamelSession *session;
3629 GSettings *settings;
3630 guint32 validity_found = 0, keep_sig_flag = 0;
3631
3632 settings = e_util_ref_settings ("org.gnome.evolution.mail");
3633 if (g_settings_get_boolean (settings, "composer-reply-keep-signature"))
3634 keep_sig_flag = E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG;
3635 g_clear_object (&settings);
3636
3637 session = e_msg_composer_ref_session (composer);
3638
3639 switch (style) {
3640 case E_MAIL_REPLY_STYLE_DO_NOT_QUOTE:
3641 /* do nothing */
3642 break;
3643 case E_MAIL_REPLY_STYLE_ATTACH:
3644 /* attach the original message as an attachment */
3645 part = mail_tool_make_message_attachment (message);
3646 e_msg_composer_attach (composer, part);
3647 g_object_unref (part);
3648 break;
3649 case E_MAIL_REPLY_STYLE_OUTLOOK:
3650 original = quoting_text (QUOTING_ORIGINAL, composer);
3651 text = em_utils_message_to_html_ex (
3652 session, message, original, E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS | keep_sig_flag,
3653 parts_list, NULL, NULL, &validity_found, out_used_part_list);
3654 e_msg_composer_set_body_text (composer, text, TRUE);
3655 g_free (text);
3656 g_free (original);
3657 emu_update_composers_security (composer, validity_found);
3658 break;
3659
3660 case E_MAIL_REPLY_STYLE_QUOTED:
3661 default:
3662 identity_source = emcu_ref_identity_source_from_composer (composer);
3663
3664 /* do what any sane user would want when replying... */
3665 credits = em_composer_utils_get_reply_credits (identity_source, message);
3666
3667 g_clear_object (&identity_source);
3668
3669 text = em_utils_message_to_html_ex (
3670 session, message, credits, E_MAIL_FORMATTER_QUOTE_FLAG_CITE | keep_sig_flag,
3671 parts_list, NULL, NULL, &validity_found, out_used_part_list);
3672 g_free (credits);
3673 e_msg_composer_set_body_text (composer, text, TRUE);
3674 g_free (text);
3675 emu_update_composers_security (composer, validity_found);
3676 break;
3677 }
3678
3679 g_object_unref (session);
3680 }
3681
3682 static gboolean
emcu_folder_is_inbox(CamelFolder * folder)3683 emcu_folder_is_inbox (CamelFolder *folder)
3684 {
3685 CamelSession *session;
3686 CamelStore *store;
3687 gboolean is_inbox = FALSE;
3688
3689 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3690
3691 store = camel_folder_get_parent_store (folder);
3692 if (!store)
3693 return FALSE;
3694
3695 session = camel_service_ref_session (CAMEL_SERVICE (store));
3696 if (!session)
3697 return FALSE;
3698
3699 if (E_IS_MAIL_SESSION (session)) {
3700 MailFolderCache *folder_cache;
3701 CamelFolderInfoFlags flags = 0;
3702
3703 folder_cache = e_mail_session_get_folder_cache (E_MAIL_SESSION (session));
3704 if (folder_cache && mail_folder_cache_get_folder_info_flags (
3705 folder_cache, store, camel_folder_get_full_name (folder), &flags)) {
3706 is_inbox = (flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX;
3707 }
3708 }
3709
3710 g_object_unref (session);
3711
3712 return is_inbox;
3713 }
3714
3715 typedef struct _AltReplyContext {
3716 EShell *shell;
3717 EAlertSink *alert_sink;
3718 CamelMimeMessage *source_message;
3719 CamelFolder *folder;
3720 gchar *message_uid;
3721 CamelMimeMessage *new_message; /* When processed with a template */
3722 EMailPartList *source;
3723 EMailReplyType type;
3724 EMailReplyStyle style;
3725 guint32 flags;
3726 gboolean template_preserve_subject;
3727 EMailPartValidityFlags validity_pgp_sum;
3728 EMailPartValidityFlags validity_smime_sum;
3729 } AltReplyContext;
3730
3731 static void
alt_reply_context_free(gpointer ptr)3732 alt_reply_context_free (gpointer ptr)
3733 {
3734 AltReplyContext *context = ptr;
3735
3736 if (context) {
3737 g_clear_object (&context->shell);
3738 g_clear_object (&context->alert_sink);
3739 g_clear_object (&context->source_message);
3740 g_clear_object (&context->folder);
3741 g_clear_object (&context->source);
3742 g_clear_object (&context->new_message);
3743 g_free (context->message_uid);
3744 g_slice_free (AltReplyContext, context);
3745 }
3746 }
3747
3748 static guint32
get_composer_mark_read_on_reply_flag(void)3749 get_composer_mark_read_on_reply_flag (void)
3750 {
3751 GSettings *settings;
3752 guint32 res = 0;
3753
3754 settings = e_util_ref_settings ("org.gnome.evolution.mail");
3755
3756 if (g_settings_get_boolean (settings, "composer-mark-read-on-reply"))
3757 res = CAMEL_MESSAGE_SEEN;
3758
3759 g_object_unref (settings);
3760
3761 return res;
3762 }
3763
3764 static void
alt_reply_composer_created_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)3765 alt_reply_composer_created_cb (GObject *source_object,
3766 GAsyncResult *result,
3767 gpointer user_data)
3768 {
3769 AltReplyContext *context = user_data;
3770 EMsgComposer *composer;
3771 GError *error = NULL;
3772
3773 g_return_if_fail (context != NULL);
3774
3775 composer = e_msg_composer_new_finish (result, &error);
3776
3777 if (composer) {
3778 EContentEditor *cnt_editor;
3779
3780 cnt_editor = e_html_editor_get_content_editor (e_msg_composer_get_editor (composer));
3781
3782 if (context->new_message) {
3783 CamelInternetAddress *to = NULL, *cc = NULL;
3784 CamelNNTPAddress *postto = NULL;
3785 gboolean need_reply_all = FALSE;
3786
3787 if ((context->flags & (E_MAIL_REPLY_FLAG_FORMAT_PLAIN | E_MAIL_REPLY_FLAG_FORMAT_HTML)) != 0) {
3788 e_content_editor_set_html_mode (cnt_editor, (context->flags & E_MAIL_REPLY_FLAG_FORMAT_HTML) != 0);
3789 }
3790
3791 em_utils_edit_message (composer, context->folder, context->new_message, context->message_uid, TRUE);
3792
3793 if (context->type == E_MAIL_REPLY_TO_SENDER) {
3794 /* Reply to sender */
3795 to = camel_internet_address_new ();
3796 if (context->folder)
3797 postto = camel_nntp_address_new ();
3798 get_reply_sender (context->source_message, to, postto);
3799 } else if (context->type == E_MAIL_REPLY_TO_LIST) {
3800 /* Reply to list */
3801 to = camel_internet_address_new ();
3802
3803 if (!get_reply_list (context->source_message, to)) {
3804 need_reply_all = TRUE;
3805 g_clear_object (&to);
3806 }
3807 } else if (context->type != E_MAIL_REPLY_TO_ALL) {
3808 g_warn_if_reached ();
3809 }
3810
3811 if (context->type == E_MAIL_REPLY_TO_ALL || need_reply_all) {
3812 /* Reply to all */
3813 to = camel_internet_address_new ();
3814 cc = camel_internet_address_new ();
3815
3816 if (context->folder)
3817 postto = camel_nntp_address_new ();
3818
3819 em_utils_get_reply_all (e_shell_get_registry (context->shell), context->source_message, to, cc, postto);
3820 }
3821
3822 reply_setup_composer_recipients (composer, to, cc, context->folder, context->message_uid, postto);
3823
3824 composer_set_no_change (composer);
3825
3826 g_clear_object (&to);
3827 g_clear_object (&cc);
3828 g_clear_object (&postto);
3829
3830 if (context->folder && context->message_uid) {
3831 emu_set_source_headers (composer, context->folder, context->message_uid,
3832 CAMEL_MESSAGE_ANSWERED | get_composer_mark_read_on_reply_flag ());
3833 }
3834 } else {
3835 em_utils_reply_to_message (composer, context->source_message,
3836 context->folder, context->message_uid, context->type, context->style,
3837 context->source, NULL, context->flags | E_MAIL_REPLY_FLAG_FORCE_SENDER_REPLY);
3838 }
3839
3840 em_composer_utils_update_security (composer, context->validity_pgp_sum, context->validity_smime_sum);
3841 } else {
3842 e_alert_submit (context->alert_sink, "mail-composer:failed-create-composer",
3843 error ? error->message : _("Unknown error"), NULL);
3844 }
3845
3846 alt_reply_context_free (context);
3847 g_clear_error (&error);
3848 }
3849
3850 static void
alt_reply_template_applied_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)3851 alt_reply_template_applied_cb (GObject *source_object,
3852 GAsyncResult *result,
3853 gpointer user_data)
3854 {
3855 AltReplyContext *context = user_data;
3856 GError *error = NULL;
3857
3858 g_return_if_fail (context != NULL);
3859
3860 context->new_message = e_mail_templates_apply_finish (source_object, result, &error);
3861
3862 if (context->new_message) {
3863 if (context->template_preserve_subject) {
3864 gchar *subject;
3865
3866 subject = emcu_construct_reply_subject (camel_mime_message_get_subject (context->source_message));
3867 camel_mime_message_set_subject (context->new_message, subject);
3868 g_free (subject);
3869 }
3870
3871 e_msg_composer_new (context->shell, alt_reply_composer_created_cb, context);
3872 } else {
3873 e_alert_submit (context->alert_sink, "mail:no-retrieve-message",
3874 error ? error->message : _("Unknown error"), NULL);
3875 alt_reply_context_free (context);
3876 }
3877
3878 g_clear_error (&error);
3879 }
3880
3881 static void
emcu_three_state_toggled_cb(GtkToggleButton * widget,gpointer user_data)3882 emcu_three_state_toggled_cb (GtkToggleButton *widget,
3883 gpointer user_data)
3884 {
3885 glong *phandlerid = user_data;
3886
3887 g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
3888 g_return_if_fail (phandlerid != NULL);
3889
3890 g_signal_handler_block (widget, *phandlerid);
3891
3892 if (gtk_toggle_button_get_inconsistent (widget) &&
3893 gtk_toggle_button_get_active (widget)) {
3894 gtk_toggle_button_set_active (widget, FALSE);
3895 gtk_toggle_button_set_inconsistent (widget, FALSE);
3896 } else if (!gtk_toggle_button_get_active (widget)) {
3897 gtk_toggle_button_set_inconsistent (widget, TRUE);
3898 gtk_toggle_button_set_active (widget, FALSE);
3899 } else {
3900 }
3901
3902 g_signal_handler_unblock (widget, *phandlerid);
3903 }
3904
3905 static void
emcu_connect_three_state_changer(GtkToggleButton * toggle_button)3906 emcu_connect_three_state_changer (GtkToggleButton *toggle_button)
3907 {
3908 glong *phandlerid;
3909
3910 g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
3911
3912 phandlerid = g_new0 (glong, 1);
3913
3914 *phandlerid = g_signal_connect_data (toggle_button, "toggled",
3915 G_CALLBACK (emcu_three_state_toggled_cb),
3916 phandlerid, (GClosureNotify) g_free, 0);
3917 }
3918
3919 static void
emcu_three_state_set_value(GtkToggleButton * toggle_button,EThreeState value)3920 emcu_three_state_set_value (GtkToggleButton *toggle_button,
3921 EThreeState value)
3922 {
3923 g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
3924
3925 if (value == E_THREE_STATE_OFF) {
3926 gtk_toggle_button_set_active (toggle_button, FALSE);
3927 gtk_toggle_button_set_inconsistent (toggle_button, FALSE);
3928 } else if (value == E_THREE_STATE_ON) {
3929 gtk_toggle_button_set_active (toggle_button, TRUE);
3930 gtk_toggle_button_set_inconsistent (toggle_button, FALSE);
3931 } else {
3932 gtk_toggle_button_set_active (toggle_button, FALSE);
3933 gtk_toggle_button_set_inconsistent (toggle_button, TRUE);
3934 }
3935 }
3936
3937 static EThreeState
emcu_three_state_get_value(GtkToggleButton * toggle_button)3938 emcu_three_state_get_value (GtkToggleButton *toggle_button)
3939 {
3940 g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button), E_THREE_STATE_INCONSISTENT);
3941
3942 if (gtk_toggle_button_get_inconsistent (toggle_button))
3943 return E_THREE_STATE_INCONSISTENT;
3944 else if (gtk_toggle_button_get_active (toggle_button))
3945 return E_THREE_STATE_ON;
3946
3947 return E_THREE_STATE_OFF;
3948 }
3949
3950 static GtkComboBox *
emcu_create_templates_combo(EShell * shell,const gchar * folder_uri,const gchar * message_uid)3951 emcu_create_templates_combo (EShell *shell,
3952 const gchar *folder_uri,
3953 const gchar *message_uid)
3954 {
3955 GtkComboBox *combo;
3956 GtkCellRenderer *renderer;
3957 EShellBackend *shell_backend;
3958 EMailSession *mail_session;
3959 EMailTemplatesStore *templates_store;
3960 GtkTreeStore *tree_store;
3961 GtkTreeIter found_iter;
3962 gboolean found_message = FALSE;
3963
3964 shell_backend = e_shell_get_backend_by_name (shell, "mail");
3965 g_return_val_if_fail (E_IS_MAIL_BACKEND (shell_backend), NULL);
3966
3967 mail_session = e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend));
3968 templates_store = e_mail_templates_store_ref_default (e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (mail_session)));
3969
3970 tree_store = e_mail_templates_store_build_model (templates_store, folder_uri, message_uid, &found_message, &found_iter);
3971
3972 combo = GTK_COMBO_BOX (gtk_combo_box_new_with_model (GTK_TREE_MODEL (tree_store)));
3973
3974 renderer = gtk_cell_renderer_text_new ();
3975 g_object_set (G_OBJECT (renderer),
3976 "ellipsize", PANGO_ELLIPSIZE_END,
3977 "single-paragraph-mode", TRUE,
3978 NULL);
3979
3980 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
3981 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
3982 "text", E_MAIL_TEMPLATES_STORE_COLUMN_DISPLAY_NAME,
3983 NULL);
3984
3985 g_clear_object (&templates_store);
3986 g_clear_object (&tree_store);
3987
3988 if (found_message) {
3989 gtk_combo_box_set_active_iter (combo, &found_iter);
3990 } else {
3991 gtk_widget_set_sensitive (GTK_WIDGET (combo), FALSE);
3992 }
3993
3994 return combo;
3995 }
3996
3997 /**
3998 * em_utils_reply_alternative:
3999 * @parent: (nullable): a parent #GtkWindow for the question dialog
4000 * @shell: an #EShell instance used to create #EMsgComposer
4001 * @alert_sink: an #EAlertSink to put any errors to
4002 * @message: a #CamelMimeMessage
4003 * @folder: (nullable): a #CamelFolder, or %NULL
4004 * @message_uid: (nullable): the UID of @message, or %NULL
4005 * @style: the reply style to use
4006 * @source: (nullable): source to inherit view settings from
4007 * @validity_pgp_sum: a bit-or of #EMailPartValidityFlags for PGP from original message part list
4008 * @validity_smime_sum: a bit-or of #EMailPartValidityFlags for S/MIME from original message part list
4009 *
4010 * This is similar to em_utils_reply_to_message(), except it asks user to
4011 * change some settings before sending. It calls em_utils_reply_to_message()
4012 * at the end for non-templated replies.
4013 *
4014 * Since: 3.30
4015 **/
4016 void
em_utils_reply_alternative(GtkWindow * parent,EShell * shell,EAlertSink * alert_sink,CamelMimeMessage * message,CamelFolder * folder,const gchar * message_uid,EMailReplyStyle default_style,EMailPartList * source,EMailPartValidityFlags validity_pgp_sum,EMailPartValidityFlags validity_smime_sum)4017 em_utils_reply_alternative (GtkWindow *parent,
4018 EShell *shell,
4019 EAlertSink *alert_sink,
4020 CamelMimeMessage *message,
4021 CamelFolder *folder,
4022 const gchar *message_uid,
4023 EMailReplyStyle default_style,
4024 EMailPartList *source,
4025 EMailPartValidityFlags validity_pgp_sum,
4026 EMailPartValidityFlags validity_smime_sum)
4027 {
4028 GtkWidget *dialog, *widget, *style_label;
4029 GtkBox *hbox, *vbox;
4030 GtkRadioButton *recip_sender, *recip_list, *recip_all;
4031 GtkLabel *sender_label, *list_label, *all_label;
4032 GtkRadioButton *style_default, *style_attach, *style_inline, *style_quote, *style_no_quote;
4033 GtkToggleButton *html_format;
4034 GtkToggleButton *bottom_posting;
4035 GtkToggleButton *top_signature;
4036 GtkCheckButton *apply_template;
4037 GtkComboBox *templates;
4038 GtkCheckButton *preserve_message_subject;
4039 PangoAttrList *attr_list;
4040 GSettings *settings;
4041 gchar *last_tmpl_folder_uri, *last_tmpl_message_uid, *address, *text;
4042 gboolean can_reply_list = FALSE;
4043 CamelInternetAddress *to, *cc;
4044 CamelNNTPAddress *postto = NULL;
4045 gint n_addresses;
4046
4047 g_return_if_fail (E_IS_SHELL (shell));
4048 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
4049
4050 settings = e_util_ref_settings ("org.gnome.evolution.mail");
4051
4052 dialog = gtk_dialog_new_with_buttons (_("Alternative Reply"), parent,
4053 GTK_DIALOG_DESTROY_WITH_PARENT,
4054 _("_Cancel"), GTK_RESPONSE_CANCEL,
4055 _("_Reply"), GTK_RESPONSE_OK,
4056 NULL);
4057
4058 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
4059
4060 vbox = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog)));
4061 gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
4062 gtk_box_set_spacing (vbox, 2);
4063
4064 #define add_with_indent(x) \
4065 gtk_widget_set_margin_left (GTK_WIDGET (x), 12); \
4066 gtk_box_pack_start (vbox, GTK_WIDGET (x), FALSE, FALSE, 0);
4067
4068 widget = gtk_label_new (_("Recipients:"));
4069 g_object_set (G_OBJECT (widget),
4070 "hexpand", FALSE,
4071 "halign", GTK_ALIGN_START,
4072 NULL);
4073 gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
4074
4075 attr_list = pango_attr_list_new ();
4076 pango_attr_list_insert (attr_list, pango_attr_style_new (PANGO_STYLE_ITALIC));
4077
4078 #define add_with_label(wgt, lbl) G_STMT_START { \
4079 GtkWidget *divider_label = gtk_label_new (":"); \
4080 gtk_label_set_attributes (GTK_LABEL (lbl), attr_list); \
4081 hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6)); \
4082 gtk_box_pack_start (hbox, GTK_WIDGET (wgt), FALSE, FALSE, 0); \
4083 gtk_box_pack_start (hbox, GTK_WIDGET (divider_label), FALSE, FALSE, 0); \
4084 gtk_box_pack_start (hbox, GTK_WIDGET (lbl), FALSE, FALSE, 0); \
4085 e_binding_bind_property ( \
4086 wgt, "sensitive", \
4087 divider_label, "visible", \
4088 G_BINDING_SYNC_CREATE); \
4089 e_binding_bind_property ( \
4090 wgt, "sensitive", \
4091 lbl, "visible", \
4092 G_BINDING_SYNC_CREATE); \
4093 add_with_indent (hbox); } G_STMT_END
4094
4095 recip_sender = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4096 NULL, _("Reply to _Sender")));
4097 sender_label = GTK_LABEL (gtk_label_new (""));
4098 add_with_label (recip_sender, sender_label);
4099
4100 recip_list = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4101 gtk_radio_button_get_group (recip_sender), _("Reply to _List")));
4102 list_label = GTK_LABEL (gtk_label_new (""));
4103 add_with_label (recip_list, list_label);
4104
4105 recip_all = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4106 gtk_radio_button_get_group (recip_sender), _("Reply to _All")));
4107 all_label = GTK_LABEL (gtk_label_new (""));
4108 add_with_label (recip_all, all_label);
4109
4110 #undef add_with_label
4111
4112 pango_attr_list_unref (attr_list);
4113
4114 /* One line gap between sections */
4115 widget = gtk_label_new (" ");
4116 gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
4117
4118 style_label = gtk_label_new (_("Reply style:"));
4119 g_object_set (G_OBJECT (style_label),
4120 "hexpand", FALSE,
4121 "halign", GTK_ALIGN_START,
4122 NULL);
4123 gtk_box_pack_start (vbox, style_label, FALSE, FALSE, 0);
4124
4125 style_default = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4126 NULL, _("_Default")));
4127 add_with_indent (style_default);
4128
4129 style_attach = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4130 gtk_radio_button_get_group (style_default), _("Attach_ment")));
4131 add_with_indent (style_attach);
4132
4133 style_inline = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4134 gtk_radio_button_get_group (style_default), _("Inline (_Outlook style)")));
4135 add_with_indent (style_inline);
4136
4137 style_quote = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4138 gtk_radio_button_get_group (style_default), _("_Quote")));
4139 add_with_indent (style_quote);
4140
4141 style_no_quote = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4142 gtk_radio_button_get_group (style_default), _("Do _Not Quote")));
4143 add_with_indent (style_no_quote);
4144
4145 /* One line gap between sections */
4146 widget = gtk_label_new (" ");
4147 gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
4148
4149 html_format = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("_Format message in HTML")));
4150 gtk_box_pack_start (vbox, GTK_WIDGET (html_format), FALSE, FALSE, 0);
4151
4152 bottom_posting = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("Start _typing at the bottom")));
4153 gtk_box_pack_start (vbox, GTK_WIDGET (bottom_posting), FALSE, FALSE, 0);
4154
4155 top_signature = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("_Keep signature above the original message")));
4156 gtk_box_pack_start (vbox, GTK_WIDGET (top_signature), FALSE, FALSE, 0);
4157
4158 /* One line gap between sections */
4159 widget = gtk_label_new (" ");
4160 gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
4161
4162 apply_template = GTK_CHECK_BUTTON (gtk_check_button_new_with_mnemonic (_("Apply t_emplate")));
4163 gtk_box_pack_start (vbox, GTK_WIDGET (apply_template), FALSE, FALSE, 0);
4164
4165 last_tmpl_folder_uri = g_settings_get_string (settings, "alt-reply-template-folder-uri");
4166 last_tmpl_message_uid = g_settings_get_string (settings, "alt-reply-template-message-uid");
4167
4168 templates = emcu_create_templates_combo (shell, last_tmpl_folder_uri, last_tmpl_message_uid);
4169 add_with_indent (templates);
4170
4171 g_free (last_tmpl_folder_uri);
4172 g_free (last_tmpl_message_uid);
4173
4174 preserve_message_subject = GTK_CHECK_BUTTON (gtk_check_button_new_with_mnemonic (_("Preserve original message S_ubject")));
4175 add_with_indent (preserve_message_subject);
4176
4177 #undef add_with_indent
4178
4179 gtk_widget_show_all (GTK_WIDGET (vbox));
4180
4181 #define populate_label_with_text(lbl, txt) \
4182 g_object_set (G_OBJECT (lbl), \
4183 "ellipsize", PANGO_ELLIPSIZE_END, \
4184 "max-width-chars", 50, \
4185 "label", txt ? txt : "", \
4186 "tooltip-text", txt ? txt : "", \
4187 NULL);
4188
4189 /* Reply to sender */
4190 to = camel_internet_address_new ();
4191 if (folder)
4192 postto = camel_nntp_address_new ();
4193 get_reply_sender (message, to, postto);
4194
4195 if (postto && camel_address_length (CAMEL_ADDRESS (postto)) > 0) {
4196 address = camel_address_format (CAMEL_ADDRESS (postto));
4197 } else {
4198 address = camel_address_format (CAMEL_ADDRESS (to));
4199 }
4200
4201 populate_label_with_text (sender_label, address);
4202
4203 g_clear_object (&postto);
4204 g_clear_object (&to);
4205 g_free (address);
4206
4207 /* Reply to list */
4208 to = camel_internet_address_new ();
4209
4210 if (!get_reply_list (message, to)) {
4211 gtk_widget_set_sensitive (GTK_WIDGET (recip_list), FALSE);
4212 } else {
4213 can_reply_list = TRUE;
4214
4215 address = camel_address_format (CAMEL_ADDRESS (to));
4216 populate_label_with_text (list_label, address);
4217 g_free (address);
4218 }
4219
4220 g_clear_object (&to);
4221
4222 /* Reply to all */
4223 to = camel_internet_address_new ();
4224 cc = camel_internet_address_new ();
4225
4226 if (folder)
4227 postto = camel_nntp_address_new ();
4228
4229 em_utils_get_reply_all (e_shell_get_registry (shell), message, to, cc, postto);
4230
4231 if (postto && camel_address_length (CAMEL_ADDRESS (postto)) > 0) {
4232 n_addresses = camel_address_length (CAMEL_ADDRESS (postto));
4233 address = camel_address_format (CAMEL_ADDRESS (postto));
4234 } else {
4235 camel_address_cat (CAMEL_ADDRESS (to), CAMEL_ADDRESS (cc));
4236 n_addresses = camel_address_length (CAMEL_ADDRESS (to));
4237 address = camel_address_format (CAMEL_ADDRESS (to));
4238 }
4239
4240 text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "one recipient", "%d recipients", n_addresses), n_addresses);
4241 populate_label_with_text (all_label, text);
4242 gtk_widget_set_tooltip_text (GTK_WIDGET (all_label), address);
4243
4244 g_clear_object (&to);
4245 g_clear_object (&cc);
4246 g_clear_object (&postto);
4247 g_free (address);
4248 g_free (text);
4249
4250 #undef populate_label_with_text
4251
4252 /* Prefer reply-to-list */
4253 if (g_settings_get_boolean (settings, "composer-group-reply-to-list")) {
4254 if (can_reply_list)
4255 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_list), TRUE);
4256 else if (n_addresses > 1)
4257 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_all), TRUE);
4258 else
4259 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_sender), TRUE);
4260
4261 /* Prefer reply-to-all */
4262 } else {
4263 if (n_addresses > 1)
4264 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_all), TRUE);
4265 else if (can_reply_list)
4266 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_list), TRUE);
4267 else
4268 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_sender), TRUE);
4269 }
4270
4271 switch (g_settings_get_enum (settings, "alt-reply-style")) {
4272 case E_MAIL_REPLY_STYLE_UNKNOWN:
4273 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_default), TRUE);
4274 break;
4275 case E_MAIL_REPLY_STYLE_QUOTED:
4276 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_quote), TRUE);
4277 break;
4278 case E_MAIL_REPLY_STYLE_DO_NOT_QUOTE:
4279 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_no_quote), TRUE);
4280 break;
4281 case E_MAIL_REPLY_STYLE_ATTACH:
4282 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_attach), TRUE);
4283 break;
4284 case E_MAIL_REPLY_STYLE_OUTLOOK:
4285 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_inline), TRUE);
4286 break;
4287 }
4288
4289 emcu_three_state_set_value (html_format, g_settings_get_enum (settings, "alt-reply-html-format"));
4290 emcu_three_state_set_value (bottom_posting, g_settings_get_enum (settings, "alt-reply-start-bottom"));
4291 emcu_three_state_set_value (top_signature, g_settings_get_enum (settings, "alt-reply-top-signature"));
4292 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template), g_settings_get_boolean (settings, "alt-reply-template-apply"));
4293 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (preserve_message_subject), g_settings_get_boolean (settings, "alt-reply-template-preserve-subject"));
4294
4295 if (!gtk_widget_get_sensitive (GTK_WIDGET (templates)))
4296 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template), FALSE);
4297
4298 emcu_connect_three_state_changer (html_format);
4299 emcu_connect_three_state_changer (bottom_posting);
4300 emcu_connect_three_state_changer (top_signature);
4301
4302 e_binding_bind_property (
4303 apply_template, "active",
4304 templates, "sensitive",
4305 G_BINDING_SYNC_CREATE);
4306
4307 e_binding_bind_property (
4308 apply_template, "active",
4309 preserve_message_subject, "sensitive",
4310 G_BINDING_SYNC_CREATE);
4311
4312 /* Enable the 'Reply Style' section only if not using Template */
4313 e_binding_bind_property (
4314 apply_template, "active",
4315 style_label, "sensitive",
4316 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4317
4318 e_binding_bind_property (
4319 apply_template, "active",
4320 style_default, "sensitive",
4321 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4322
4323 e_binding_bind_property (
4324 apply_template, "active",
4325 style_attach, "sensitive",
4326 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4327
4328 e_binding_bind_property (
4329 apply_template, "active",
4330 style_inline, "sensitive",
4331 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4332
4333 e_binding_bind_property (
4334 apply_template, "active",
4335 style_quote, "sensitive",
4336 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4337
4338 e_binding_bind_property (
4339 apply_template, "active",
4340 style_no_quote, "sensitive",
4341 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4342
4343 /* Similarly with other options, which don't work when using Templates */
4344 e_binding_bind_property (
4345 apply_template, "active",
4346 bottom_posting, "sensitive",
4347 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4348
4349 e_binding_bind_property (
4350 apply_template, "active",
4351 top_signature, "sensitive",
4352 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4353
4354 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
4355 GtkTreeIter iter;
4356 AltReplyContext *context;
4357 EThreeState three_state;
4358 CamelFolder *template_folder = NULL;
4359 gchar *template_message_uid = NULL;
4360
4361 context = g_slice_new0 (AltReplyContext);
4362 context->shell = g_object_ref (shell);
4363 context->alert_sink = g_object_ref (alert_sink);
4364 context->source_message = g_object_ref (message);
4365 context->folder = folder ? g_object_ref (folder) : NULL;
4366 context->source = source ? g_object_ref (source) : NULL;
4367 context->message_uid = g_strdup (message_uid);
4368 context->style = E_MAIL_REPLY_STYLE_UNKNOWN;
4369 context->flags = E_MAIL_REPLY_FLAG_FORCE_STYLE;
4370 context->validity_pgp_sum = validity_pgp_sum;
4371 context->validity_smime_sum = validity_smime_sum;
4372
4373 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_quote)))
4374 context->style = E_MAIL_REPLY_STYLE_QUOTED;
4375 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_no_quote)))
4376 context->style = E_MAIL_REPLY_STYLE_DO_NOT_QUOTE;
4377 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_attach)))
4378 context->style = E_MAIL_REPLY_STYLE_ATTACH;
4379 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_inline)))
4380 context->style = E_MAIL_REPLY_STYLE_OUTLOOK;
4381 else
4382 context->flags = context->flags & (~E_MAIL_REPLY_FLAG_FORCE_STYLE);
4383
4384 three_state = emcu_three_state_get_value (html_format);
4385 g_settings_set_enum (settings, "alt-reply-html-format", three_state);
4386
4387 if (three_state == E_THREE_STATE_ON)
4388 context->flags |= E_MAIL_REPLY_FLAG_FORMAT_HTML;
4389 else if (three_state == E_THREE_STATE_OFF)
4390 context->flags |= E_MAIL_REPLY_FLAG_FORMAT_PLAIN;
4391
4392 three_state = emcu_three_state_get_value (bottom_posting);
4393 g_settings_set_enum (settings, "alt-reply-start-bottom", three_state);
4394
4395 if (three_state == E_THREE_STATE_ON)
4396 context->flags |= E_MAIL_REPLY_FLAG_BOTTOM_POSTING;
4397 else if (three_state == E_THREE_STATE_OFF)
4398 context->flags |= E_MAIL_REPLY_FLAG_TOP_POSTING;
4399
4400 three_state = emcu_three_state_get_value (top_signature);
4401 g_settings_set_enum (settings, "alt-reply-top-signature", three_state);
4402
4403 if (three_state == E_THREE_STATE_ON)
4404 context->flags |= E_MAIL_REPLY_FLAG_TOP_SIGNATURE;
4405 else if (three_state == E_THREE_STATE_OFF)
4406 context->flags |= E_MAIL_REPLY_FLAG_BOTTOM_SIGNATURE;
4407
4408 g_settings_set_enum (settings, "alt-reply-style", context->style);
4409 g_settings_set_boolean (settings, "alt-reply-template-apply", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_template)));
4410 g_settings_set_boolean (settings, "alt-reply-template-preserve-subject", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preserve_message_subject)));
4411
4412 if (gtk_combo_box_get_active_iter (templates, &iter)) {
4413 gtk_tree_model_get (gtk_combo_box_get_model (templates), &iter,
4414 E_MAIL_TEMPLATES_STORE_COLUMN_FOLDER, &template_folder,
4415 E_MAIL_TEMPLATES_STORE_COLUMN_MESSAGE_UID, &template_message_uid,
4416 -1);
4417 }
4418
4419 if (template_folder) {
4420 gchar *folder_uri;
4421
4422 folder_uri = e_mail_folder_uri_from_folder (template_folder);
4423 g_settings_set_string (settings, "alt-reply-template-folder-uri", folder_uri ? folder_uri : "");
4424 g_free (folder_uri);
4425 }
4426
4427 g_settings_set_string (settings, "alt-reply-template-message-uid", template_message_uid ? template_message_uid : "");
4428
4429 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_sender)))
4430 context->type = E_MAIL_REPLY_TO_SENDER;
4431 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_list)))
4432 context->type = E_MAIL_REPLY_TO_LIST;
4433 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_all)))
4434 context->type = E_MAIL_REPLY_TO_ALL;
4435 else
4436 g_warn_if_reached ();
4437
4438 if (context->style == E_MAIL_REPLY_STYLE_UNKNOWN)
4439 context->style = default_style;
4440
4441 context->template_preserve_subject = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preserve_message_subject));
4442
4443 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_template))) {
4444 e_mail_templates_apply (context->source_message, context->folder, message_uid, template_folder, template_message_uid,
4445 NULL, alt_reply_template_applied_cb, context);
4446
4447 } else {
4448 e_msg_composer_new (context->shell, alt_reply_composer_created_cb, context);
4449 }
4450
4451 g_clear_object (&template_folder);
4452 g_free (template_message_uid);
4453 }
4454
4455 gtk_widget_destroy (dialog);
4456 g_clear_object (&settings);
4457 }
4458
4459 static void
em_utils_update_by_reply_flags(EContentEditor * cnt_editor,guint32 reply_flags)4460 em_utils_update_by_reply_flags (EContentEditor *cnt_editor,
4461 guint32 reply_flags) /* EMailReplyFlags */
4462 {
4463 if ((reply_flags & (E_MAIL_REPLY_FLAG_TOP_POSTING | E_MAIL_REPLY_FLAG_BOTTOM_POSTING)) != 0) {
4464 e_content_editor_set_start_bottom (cnt_editor,
4465 (reply_flags & E_MAIL_REPLY_FLAG_TOP_POSTING) != 0 ?
4466 E_THREE_STATE_OFF : E_THREE_STATE_ON);
4467 }
4468
4469 if ((reply_flags & (E_MAIL_REPLY_FLAG_TOP_SIGNATURE | E_MAIL_REPLY_FLAG_BOTTOM_SIGNATURE)) != 0) {
4470 e_content_editor_set_top_signature (cnt_editor,
4471 (reply_flags & E_MAIL_REPLY_FLAG_TOP_SIGNATURE) != 0 ?
4472 E_THREE_STATE_ON : E_THREE_STATE_OFF);
4473 }
4474 }
4475
4476 /**
4477 * em_utils_reply_to_message:
4478 * @composer: an #EMsgComposer
4479 * @message: a #CamelMimeMessage
4480 * @folder: a #CamelFolder, or %NULL
4481 * @message_uid: the UID of @message, or %NULL
4482 * @type: the type of reply to create
4483 * @style: the reply style to use
4484 * @source: source to inherit view settings from
4485 * @address: used for E_MAIL_REPLY_TO_RECIPIENT @type
4486 * @reply_flags: bit-or of #EMailReplyFlags
4487 *
4488 * Creates a new composer ready to reply to @message.
4489 *
4490 * @folder and @message_uid may be supplied in order to update the message
4491 * flags once it has been replied to.
4492 **/
4493 void
em_utils_reply_to_message(EMsgComposer * composer,CamelMimeMessage * message,CamelFolder * folder,const gchar * message_uid,EMailReplyType type,EMailReplyStyle style,EMailPartList * parts_list,CamelInternetAddress * address,EMailReplyFlags reply_flags)4494 em_utils_reply_to_message (EMsgComposer *composer,
4495 CamelMimeMessage *message,
4496 CamelFolder *folder,
4497 const gchar *message_uid,
4498 EMailReplyType type,
4499 EMailReplyStyle style,
4500 EMailPartList *parts_list,
4501 CamelInternetAddress *address,
4502 EMailReplyFlags reply_flags)
4503 {
4504 ESourceRegistry *registry;
4505 CamelInternetAddress *to, *cc;
4506 CamelNNTPAddress *postto = NULL;
4507 EShell *shell;
4508 ESourceMailCompositionReplyStyle prefer_reply_style = E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT;
4509 ESource *source;
4510 EMailPartList *used_part_list = NULL;
4511 EContentEditor *cnt_editor;
4512 gchar *identity_uid = NULL, *identity_name = NULL, *identity_address = NULL;
4513 guint32 flags;
4514
4515 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
4516 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
4517
4518 cnt_editor = e_html_editor_get_content_editor (e_msg_composer_get_editor (composer));
4519
4520 if ((reply_flags & (E_MAIL_REPLY_FLAG_FORMAT_PLAIN | E_MAIL_REPLY_FLAG_FORMAT_HTML)) != 0) {
4521 e_content_editor_set_html_mode (cnt_editor, (reply_flags & E_MAIL_REPLY_FLAG_FORMAT_HTML) != 0);
4522 }
4523
4524 em_utils_update_by_reply_flags (cnt_editor, reply_flags);
4525
4526 to = camel_internet_address_new ();
4527 cc = camel_internet_address_new ();
4528
4529 shell = e_msg_composer_get_shell (composer);
4530 registry = e_shell_get_registry (shell);
4531
4532 if (type == E_MAIL_REPLY_TO_SENDER &&
4533 !(reply_flags & E_MAIL_REPLY_FLAG_FORCE_SENDER_REPLY) &&
4534 em_utils_sender_is_user (registry, message, TRUE)) {
4535 type = E_MAIL_REPLY_TO_ALL;
4536 }
4537
4538 source = em_composer_utils_guess_identity_source (shell, message, folder, message_uid, &identity_name, &identity_address);
4539
4540 if (source != NULL) {
4541 identity_uid = e_source_dup_uid (source);
4542 if (!(reply_flags & E_MAIL_REPLY_FLAG_FORCE_STYLE) &&
4543 e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) {
4544 ESourceMailComposition *extension;
4545
4546 extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION);
4547 prefer_reply_style = e_source_mail_composition_get_reply_style (extension);
4548 }
4549
4550 g_object_unref (source);
4551 }
4552
4553 flags = CAMEL_MESSAGE_ANSWERED | get_composer_mark_read_on_reply_flag ();
4554
4555 if (!address && (type == E_MAIL_REPLY_TO_FROM || type == E_MAIL_REPLY_TO_SENDER) &&
4556 folder && !emcu_folder_is_inbox (folder) && em_utils_folder_is_sent (registry, folder))
4557 type = E_MAIL_REPLY_TO_ALL;
4558
4559 switch (type) {
4560 case E_MAIL_REPLY_TO_FROM:
4561 if (folder)
4562 postto = camel_nntp_address_new ();
4563
4564 get_reply_from (message, to, postto);
4565 break;
4566 case E_MAIL_REPLY_TO_RECIPIENT:
4567 if (folder)
4568 postto = camel_nntp_address_new ();
4569
4570 get_reply_recipient (message, to, postto, address);
4571 break;
4572 case E_MAIL_REPLY_TO_SENDER:
4573 if (folder)
4574 postto = camel_nntp_address_new ();
4575
4576 get_reply_sender (message, to, postto);
4577 break;
4578 case E_MAIL_REPLY_TO_LIST:
4579 flags |= CAMEL_MESSAGE_ANSWERED_ALL;
4580 if (get_reply_list (message, to))
4581 break;
4582 /* falls through */
4583 case E_MAIL_REPLY_TO_ALL:
4584 flags |= CAMEL_MESSAGE_ANSWERED_ALL;
4585 if (folder)
4586 postto = camel_nntp_address_new ();
4587
4588 em_utils_get_reply_all (registry, message, to, cc, postto);
4589 break;
4590 }
4591
4592 reply_setup_composer (composer, message, identity_uid, identity_name, identity_address, to, cc, folder, message_uid, postto);
4593
4594 if (postto)
4595 g_object_unref (postto);
4596 g_object_unref (to);
4597 g_object_unref (cc);
4598
4599 /* If there was no send-account override */
4600 if (!identity_uid) {
4601 EComposerHeaderTable *header_table;
4602 gchar *used_identity_uid;
4603
4604 header_table = e_msg_composer_get_header_table (composer);
4605 used_identity_uid = e_composer_header_table_dup_identity_uid (header_table, NULL, NULL);
4606
4607 if (used_identity_uid) {
4608 source = e_source_registry_ref_source (registry, used_identity_uid);
4609 if (source) {
4610 if (!(reply_flags & E_MAIL_REPLY_FLAG_FORCE_STYLE) &&
4611 e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) {
4612 ESourceMailComposition *extension;
4613
4614 extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION);
4615 prefer_reply_style = e_source_mail_composition_get_reply_style (extension);
4616 }
4617
4618 g_object_unref (source);
4619 }
4620 }
4621
4622 g_free (used_identity_uid);
4623 }
4624
4625 switch (prefer_reply_style) {
4626 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT:
4627 /* Do nothing, keep the passed-in reply style. */
4628 break;
4629 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_QUOTED:
4630 style = E_MAIL_REPLY_STYLE_QUOTED;
4631 break;
4632 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DO_NOT_QUOTE:
4633 style = E_MAIL_REPLY_STYLE_DO_NOT_QUOTE;
4634 break;
4635 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_ATTACH:
4636 style = E_MAIL_REPLY_STYLE_ATTACH;
4637 break;
4638 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_OUTLOOK:
4639 style = E_MAIL_REPLY_STYLE_OUTLOOK;
4640 break;
4641 }
4642
4643 composer_set_body (composer, message, style, parts_list, &used_part_list);
4644
4645 e_msg_composer_add_attachments_from_part_list (composer, used_part_list, TRUE);
4646 g_clear_object (&used_part_list);
4647
4648 if (folder)
4649 emu_set_source_headers (composer, folder, message_uid, flags);
4650
4651 /* because some reply types can change recipients after the composer is populated */
4652 em_utils_apply_send_account_override_to_composer (composer, folder);
4653
4654 /* This is required to be done (also) at the end */
4655 em_utils_update_by_reply_flags (cnt_editor, reply_flags);
4656
4657 composer_set_no_change (composer);
4658
4659 gtk_widget_show (GTK_WIDGET (composer));
4660
4661 g_free (identity_uid);
4662 g_free (identity_name);
4663 g_free (identity_address);
4664 }
4665
4666 static void
post_header_clicked_cb(EComposerPostHeader * header,EMailSession * session)4667 post_header_clicked_cb (EComposerPostHeader *header,
4668 EMailSession *session)
4669 {
4670 GtkTreeSelection *selection;
4671 EMFolderSelector *selector;
4672 EMFolderTreeModel *model;
4673 EMFolderTree *folder_tree;
4674 GtkWidget *dialog;
4675 GList *list;
4676 const gchar *caption;
4677
4678 /* FIXME Limit the folder tree to the NNTP account? */
4679 model = em_folder_tree_model_get_default ();
4680
4681 dialog = em_folder_selector_new (
4682 /* FIXME GTK_WINDOW (composer) */ NULL, model);
4683
4684 gtk_window_set_title (GTK_WINDOW (dialog), _("Posting destination"));
4685
4686 selector = EM_FOLDER_SELECTOR (dialog);
4687 em_folder_selector_set_can_create (selector, TRUE);
4688
4689 caption = _("Choose folders to post the message to.");
4690 em_folder_selector_set_caption (selector, caption);
4691
4692 folder_tree = em_folder_selector_get_folder_tree (selector);
4693
4694 em_folder_tree_set_excluded (
4695 folder_tree,
4696 EMFT_EXCLUDE_NOSELECT |
4697 EMFT_EXCLUDE_VIRTUAL |
4698 EMFT_EXCLUDE_VTRASH);
4699
4700 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
4701 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
4702
4703 list = e_composer_post_header_get_folders (header);
4704 em_folder_tree_set_selected_list (folder_tree, list, FALSE);
4705 g_list_foreach (list, (GFunc) g_free, NULL);
4706 g_list_free (list);
4707
4708 if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) {
4709 /* Prevent the header's "custom" flag from being reset,
4710 * which is what the default method will do next. */
4711 g_signal_stop_emission_by_name (header, "clicked");
4712 goto exit;
4713 }
4714
4715 list = em_folder_tree_get_selected_uris (folder_tree);
4716 e_composer_post_header_set_folders (header, list);
4717 g_list_foreach (list, (GFunc) g_free, NULL);
4718 g_list_free (list);
4719
4720 exit:
4721 gtk_widget_destroy (dialog);
4722 }
4723
4724 /**
4725 * em_configure_new_composer:
4726 * @composer: a newly created #EMsgComposer
4727 *
4728 * Integrates a newly created #EMsgComposer into the mail backend. The
4729 * composer can't link directly to the mail backend without introducing
4730 * circular library dependencies, so this function finishes configuring
4731 * things the #EMsgComposer instance can't do itself.
4732 **/
4733 void
em_configure_new_composer(EMsgComposer * composer,EMailSession * session)4734 em_configure_new_composer (EMsgComposer *composer,
4735 EMailSession *session)
4736 {
4737 EComposerHeaderTable *table;
4738 EComposerHeaderType header_type;
4739 EComposerHeader *header;
4740
4741 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
4742 g_return_if_fail (E_IS_MAIL_SESSION (session));
4743
4744 header_type = E_COMPOSER_HEADER_POST_TO;
4745 table = e_msg_composer_get_header_table (composer);
4746 header = e_composer_header_table_get_header (table, header_type);
4747
4748 g_signal_connect (
4749 composer, "presend",
4750 G_CALLBACK (composer_presend_check_recipients), session);
4751
4752 g_signal_connect (
4753 composer, "presend",
4754 G_CALLBACK (composer_presend_check_identity), session);
4755
4756 g_signal_connect (
4757 composer, "presend",
4758 G_CALLBACK (composer_presend_check_plugins), session);
4759
4760 g_signal_connect (
4761 composer, "presend",
4762 G_CALLBACK (composer_presend_check_subject), session);
4763
4764 g_signal_connect (
4765 composer, "presend",
4766 G_CALLBACK (composer_presend_check_unwanted_html), session);
4767
4768 g_signal_connect (
4769 composer, "send",
4770 G_CALLBACK (em_utils_composer_send_cb), session);
4771
4772 g_signal_connect (
4773 composer, "save-to-drafts",
4774 G_CALLBACK (em_utils_composer_save_to_drafts_cb), session);
4775
4776 g_signal_connect (
4777 composer, "save-to-outbox",
4778 G_CALLBACK (em_utils_composer_save_to_outbox_cb), session);
4779
4780 g_signal_connect (
4781 composer, "print",
4782 G_CALLBACK (em_utils_composer_print_cb), session);
4783
4784 /* Handle "Post To:" button clicks, which displays a folder tree
4785 * widget. The composer doesn't know about folder tree widgets,
4786 * so it can't handle this itself.
4787 *
4788 * Note: This is a G_SIGNAL_RUN_LAST signal, which allows us to
4789 * stop the signal emission if the user cancels or closes
4790 * the folder selector dialog. See the handler function. */
4791 g_signal_connect (
4792 header, "clicked",
4793 G_CALLBACK (post_header_clicked_cb), session);
4794 }
4795
4796 /* free returned pointer with g_object_unref(), if not NULL */
4797 ESource *
em_utils_check_send_account_override(EShell * shell,CamelMimeMessage * message,CamelFolder * folder,gchar ** out_alias_name,gchar ** out_alias_address)4798 em_utils_check_send_account_override (EShell *shell,
4799 CamelMimeMessage *message,
4800 CamelFolder *folder,
4801 gchar **out_alias_name,
4802 gchar **out_alias_address)
4803 {
4804 EMailBackend *mail_backend;
4805 EMailSendAccountOverride *account_override;
4806 CamelInternetAddress *to = NULL, *cc = NULL, *bcc = NULL;
4807 gchar *folder_uri = NULL, *account_uid, *alias_name = NULL, *alias_address = NULL;
4808 ESource *account_source = NULL;
4809 ESourceRegistry *source_registry;
4810
4811 g_return_val_if_fail (E_IS_SHELL (shell), NULL);
4812
4813 if (!message && !folder)
4814 return NULL;
4815
4816 if (message) {
4817 to = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
4818 cc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
4819 bcc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC);
4820 }
4821
4822 mail_backend = E_MAIL_BACKEND (e_shell_get_backend_by_name (shell, "mail"));
4823 g_return_val_if_fail (mail_backend != NULL, NULL);
4824
4825 if (folder)
4826 folder_uri = e_mail_folder_uri_from_folder (folder);
4827
4828 source_registry = e_shell_get_registry (shell);
4829 account_override = e_mail_backend_get_send_account_override (mail_backend);
4830 account_uid = e_mail_send_account_override_get_account_uid (account_override, folder_uri, to, cc, bcc, &alias_name, &alias_address);
4831
4832 while (account_uid) {
4833 account_source = e_source_registry_ref_source (source_registry, account_uid);
4834 if (account_source)
4835 break;
4836
4837 /* stored send account override settings contain a reference
4838 * to a dropped account, thus cleanup it now */
4839 e_mail_send_account_override_remove_for_account_uid (account_override, account_uid, alias_name, alias_address);
4840
4841 g_free (account_uid);
4842 g_free (alias_name);
4843 g_free (alias_address);
4844
4845 alias_name = NULL;
4846 alias_address = NULL;
4847
4848 account_uid = e_mail_send_account_override_get_account_uid (account_override, folder_uri, to, cc, bcc, &alias_name, &alias_address);
4849 }
4850
4851 if (out_alias_name)
4852 *out_alias_name = alias_name;
4853 else
4854 g_free (alias_name);
4855
4856 if (out_alias_address)
4857 *out_alias_address = alias_address;
4858 else
4859 g_free (alias_address);
4860
4861 g_free (folder_uri);
4862 g_free (account_uid);
4863
4864 return account_source;
4865 }
4866
4867 void
em_utils_apply_send_account_override_to_composer(EMsgComposer * composer,CamelFolder * folder)4868 em_utils_apply_send_account_override_to_composer (EMsgComposer *composer,
4869 CamelFolder *folder)
4870 {
4871 CamelMimeMessage *message;
4872 EComposerHeaderTable *header_table;
4873 EShell *shell;
4874 ESource *source;
4875 gchar *alias_name = NULL, *alias_address = NULL;
4876
4877 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
4878
4879 shell = e_msg_composer_get_shell (composer);
4880 message = em_utils_get_composer_recipients_as_message (composer);
4881 source = em_utils_check_send_account_override (shell, message, folder, &alias_name, &alias_address);
4882 g_clear_object (&message);
4883
4884 if (!source)
4885 return;
4886
4887 header_table = e_msg_composer_get_header_table (composer);
4888 e_composer_header_table_set_identity_uid (header_table, e_source_get_uid (source), alias_name, alias_address);
4889
4890 g_object_unref (source);
4891 g_free (alias_name);
4892 g_free (alias_address);
4893 }
4894
4895 void
em_utils_add_installed_languages(GtkComboBoxText * combo)4896 em_utils_add_installed_languages (GtkComboBoxText *combo)
4897 {
4898 const ESupportedLocales *supported_locales;
4899 GHashTable *locales;
4900 GList *langs = NULL, *link;
4901 gboolean has_en_us = FALSE;
4902 gint ii, n_langs = 0;
4903
4904 g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo));
4905
4906 supported_locales = e_util_get_supported_locales ();
4907 locales = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
4908
4909 for (ii = 0; supported_locales[ii].code; ii++) {
4910 const gchar *locale = supported_locales[ii].locale;
4911
4912 if (locale) {
4913 gchar *name;
4914
4915 name = e_util_get_language_name (locale);
4916
4917 if (name && *name) {
4918 g_hash_table_insert (locales, name, (gpointer) locale);
4919 } else {
4920 g_free (name);
4921 g_hash_table_insert (locales, g_strdup (locale), (gpointer) locale);
4922 }
4923
4924 has_en_us = has_en_us || g_strcmp0 (locale, "en_US") == 0;
4925 }
4926 }
4927
4928 if (!has_en_us) {
4929 const gchar *locale = "C";
4930 gchar *name = e_util_get_language_name ("en_US");
4931
4932 if (name && *name) {
4933 g_hash_table_insert (locales, name, (gpointer) locale);
4934 } else {
4935 g_free (name);
4936 g_hash_table_insert (locales, g_strdup ("en_US"), (gpointer) locale);
4937 }
4938 }
4939
4940 langs = g_hash_table_get_keys (locales);
4941 langs = g_list_sort (langs, (GCompareFunc) g_utf8_collate);
4942
4943 for (link = langs; link; link = g_list_next (link)) {
4944 const gchar *lang_name = link->data;
4945
4946 if (lang_name) {
4947 const gchar *locale = g_hash_table_lookup (locales, lang_name);
4948
4949 gtk_combo_box_text_append (combo, locale, lang_name);
4950 n_langs++;
4951 }
4952 }
4953
4954 g_hash_table_destroy (locales);
4955 g_list_free (langs);
4956
4957 if (n_langs > 10)
4958 gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (combo), 5);
4959 }
4960
4961 void
em_composer_utils_update_security(EMsgComposer * composer,EMailPartValidityFlags validity_pgp_sum,EMailPartValidityFlags validity_smime_sum)4962 em_composer_utils_update_security (EMsgComposer *composer,
4963 EMailPartValidityFlags validity_pgp_sum,
4964 EMailPartValidityFlags validity_smime_sum)
4965 {
4966 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
4967
4968 if (validity_pgp_sum != 0 || validity_smime_sum != 0) {
4969 GtkToggleAction *action;
4970 GSettings *settings;
4971 gboolean sign_reply;
4972
4973 settings = e_util_ref_settings ("org.gnome.evolution.mail");
4974 sign_reply = g_settings_get_boolean (settings, "composer-sign-reply-if-signed");
4975 g_object_unref (settings);
4976
4977 if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_PGP) != 0) {
4978 if (sign_reply && (validity_pgp_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) {
4979 action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer));
4980 gtk_toggle_action_set_active (action, TRUE);
4981 }
4982
4983 if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) {
4984 action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer));
4985 gtk_toggle_action_set_active (action, TRUE);
4986 }
4987 }
4988
4989 if ((validity_smime_sum & E_MAIL_PART_VALIDITY_SMIME) != 0) {
4990 if (sign_reply && (validity_smime_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) {
4991 action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer));
4992 gtk_toggle_action_set_active (action, TRUE);
4993 }
4994
4995 if ((validity_smime_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) {
4996 action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer));
4997 gtk_toggle_action_set_active (action, TRUE);
4998 }
4999 }
5000 }
5001 }
5002
5003 void
em_composer_utils_update_security_from_part_list(EMsgComposer * composer,EMailPartList * part_list)5004 em_composer_utils_update_security_from_part_list (EMsgComposer *composer,
5005 EMailPartList *part_list)
5006 {
5007 EMailPartValidityFlags validity_pgp_sum = 0;
5008 EMailPartValidityFlags validity_smime_sum = 0;
5009
5010 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
5011
5012 if (!part_list)
5013 return;
5014
5015 e_mail_part_list_sum_validity (part_list, &validity_pgp_sum, &validity_smime_sum);
5016 em_composer_utils_update_security (composer, validity_pgp_sum, validity_smime_sum);
5017 }
5018