No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-mail-reader-utils.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-mail-reader-utils.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * e-mail-reader-utils.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 /* Miscellaneous utility functions used by EMailReader actions. */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include "e-mail-reader-utils.h"
29
30 #include <glib/gi18n.h>
31 #include <libxml/tree.h>
32 #include <gtkhtml/gtkhtml.h>
33 #include <camel/camel.h>
34
35 #include "libevolution-utils/e-alert-dialog.h"
36 #include "filter/e-filter-rule.h"
37 #include "misc/e-web-view.h"
38 #include "shell/e-shell-utils.h"
39
40 #include <libemail-engine/e-mail-folder-utils.h>
41 #include <libemail-engine/e-mail-utils.h>
42 #include <libemail-engine/mail-ops.h>
43 #include <libemail-engine/mail-tools.h>
44
45 #include "mail/e-mail-backend.h"
46 #include "mail/e-mail-browser.h"
47 #include "mail/e-mail-printer.h"
48 #include "mail/e-mail-display.h"
49 #include "mail/em-composer-utils.h"
50 #include "mail/em-utils.h"
51 #include "mail/mail-autofilter.h"
52 #include "mail/mail-vfolder-ui.h"
53 #include "mail/message-list.h"
54
55 #include <em-format/e-mail-parser.h>
56 #include <em-format/e-mail-part-utils.h>
57
58 #define d(x)
59
60 typedef struct _AsyncContext AsyncContext;
61
62 struct _AsyncContext {
63 EActivity *activity;
64 CamelFolder *folder;
65 CamelMimeMessage *message;
66 EMailPartList *part_list;
67 EMailReader *reader;
68 CamelInternetAddress *address;
69 gchar *folder_name;
70 gchar *message_uid;
71
72 EMailReplyType reply_type;
73 EMailReplyStyle reply_style;
74 GtkPrintOperationAction print_action;
75 const gchar *filter_source;
76 gint filter_type;
77 };
78
79 static void
80 async_context_free (AsyncContext *context)
81 {
82 if (context->activity != NULL)
83 g_object_unref (context->activity);
84
85 if (context->folder != NULL)
86 g_object_unref (context->folder);
87
88 if (context->message != NULL)
89 g_object_unref (context->message);
90
91 if (context->part_list != NULL)
92 g_object_unref (context->part_list);
93
94 if (context->reader != NULL)
95 g_object_unref (context->reader);
96
97 if (context->address != NULL)
98 g_object_unref (context->address);
99
100 g_free (context->folder_name);
101 g_free (context->message_uid);
102
103 g_slice_free (AsyncContext, context);
104 }
105
106 static gboolean
107 mail_reader_is_special_local_folder (const gchar *name)
108 {
109 return (strcmp (name, "Drafts") == 0 ||
110 strcmp (name, "Inbox") == 0 ||
111 strcmp (name, "Outbox") == 0 ||
112 strcmp (name, "Sent") == 0 ||
113 strcmp (name, "Templates") == 0);
114 }
115
116 gboolean
117 e_mail_reader_confirm_delete (EMailReader *reader)
118 {
119 EShell *shell;
120 EMailBackend *backend;
121 EShellBackend *shell_backend;
122 EShellSettings *shell_settings;
123 CamelFolder *folder;
124 CamelStore *parent_store;
125 GtkWidget *check_button;
126 GtkWidget *container;
127 GtkWidget *dialog;
128 GtkWindow *window;
129 const gchar *label;
130 gboolean prompt_delete_in_vfolder;
131 gint response;
132
133 /* Remind users what deleting from a search folder does. */
134
135 g_return_val_if_fail (E_IS_MAIL_READER (reader), FALSE);
136
137 backend = e_mail_reader_get_backend (reader);
138 folder = e_mail_reader_get_folder (reader);
139 window = e_mail_reader_get_window (reader);
140
141 shell_backend = E_SHELL_BACKEND (backend);
142 shell = e_shell_backend_get_shell (shell_backend);
143 shell_settings = e_shell_get_shell_settings (shell);
144
145 prompt_delete_in_vfolder = e_shell_settings_get_boolean (
146 shell_settings, "mail-prompt-delete-in-vfolder");
147
148 parent_store = camel_folder_get_parent_store (folder);
149
150 if (!CAMEL_IS_VEE_STORE (parent_store))
151 return TRUE;
152
153 if (!prompt_delete_in_vfolder)
154 return TRUE;
155
156 dialog = e_alert_dialog_new_for_args (
157 window, "mail:ask-delete-vfolder-msg",
158 camel_folder_get_full_name (folder), NULL);
159
160 container = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));
161
162 label = _("Do not warn me again");
163 check_button = gtk_check_button_new_with_label (label);
164 gtk_box_pack_start (GTK_BOX (container), check_button, TRUE, TRUE, 6);
165 gtk_widget_show (check_button);
166
167 response = gtk_dialog_run (GTK_DIALOG (dialog));
168
169 if (response != GTK_RESPONSE_DELETE_EVENT)
170 e_shell_settings_set_boolean (
171 shell_settings,
172 "mail-prompt-delete-in-vfolder",
173 !gtk_toggle_button_get_active (
174 GTK_TOGGLE_BUTTON (check_button)));
175
176 gtk_widget_destroy (dialog);
177
178 return (response == GTK_RESPONSE_OK);
179 }
180
181 static void
182 mail_reader_delete_folder_cb (CamelFolder *folder,
183 GAsyncResult *result,
184 AsyncContext *context)
185 {
186 EAlertSink *alert_sink;
187 GError *error = NULL;
188
189 alert_sink = e_activity_get_alert_sink (context->activity);
190
191 e_mail_folder_remove_finish (folder, result, &error);
192
193 if (e_activity_handle_cancellation (context->activity, error)) {
194 g_error_free (error);
195
196 } else if (error != NULL) {
197 e_alert_submit (
198 alert_sink, "mail:no-delete-folder",
199 camel_folder_get_full_name (folder),
200 error->message, NULL);
201 g_error_free (error);
202
203 } else {
204 e_activity_set_state (
205 context->activity, E_ACTIVITY_COMPLETED);
206 }
207
208 async_context_free (context);
209 }
210
211 void
212 e_mail_reader_delete_folder (EMailReader *reader,
213 CamelFolder *folder)
214 {
215 EMailBackend *backend;
216 EMailSession *session;
217 EShell *shell;
218 EAlertSink *alert_sink;
219 CamelStore *parent_store;
220 CamelProvider *provider;
221 MailFolderCache *folder_cache;
222 GtkWindow *parent = e_shell_get_active_window (NULL);
223 GtkWidget *dialog;
224 gboolean store_is_local;
225 const gchar *display_name;
226 const gchar *full_name;
227 CamelFolderInfoFlags flags = 0;
228 gboolean have_flags;
229
230 g_return_if_fail (E_IS_MAIL_READER (reader));
231 g_return_if_fail (CAMEL_IS_FOLDER (folder));
232
233 full_name = camel_folder_get_full_name (folder);
234 display_name = camel_folder_get_display_name (folder);
235 parent_store = camel_folder_get_parent_store (folder);
236 provider = camel_service_get_provider (CAMEL_SERVICE (parent_store));
237
238 store_is_local = (provider->flags & CAMEL_PROVIDER_IS_LOCAL) != 0;
239
240 backend = e_mail_reader_get_backend (reader);
241 session = e_mail_backend_get_session (backend);
242
243 alert_sink = e_mail_reader_get_alert_sink (reader);
244 folder_cache = e_mail_session_get_folder_cache (session);
245
246 if (store_is_local &&
247 mail_reader_is_special_local_folder (full_name)) {
248 e_alert_submit (
249 alert_sink, "mail:no-delete-special-folder",
250 display_name, NULL);
251 return;
252 }
253
254 shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
255
256 if (!store_is_local && !e_shell_get_online (shell))
257 {
258 e_alert_submit (
259 alert_sink, "mail:online-operation",
260 display_name, NULL);
261 return;
262 }
263
264 have_flags = mail_folder_cache_get_folder_info_flags (
265 folder_cache, folder, &flags);
266
267 if (have_flags && (flags & CAMEL_FOLDER_SYSTEM)) {
268 e_alert_submit (
269 alert_sink, "mail:no-delete-special-folder",
270 display_name, NULL);
271 return;
272 }
273
274 if (have_flags && (flags & CAMEL_FOLDER_CHILDREN)) {
275 if (CAMEL_IS_VEE_STORE (parent_store))
276 dialog = e_alert_dialog_new_for_args (
277 parent, "mail:ask-delete-vfolder",
278 display_name, NULL);
279 else
280 dialog = e_alert_dialog_new_for_args (
281 parent, "mail:ask-delete-folder",
282 display_name, NULL);
283 } else {
284 if (CAMEL_IS_VEE_STORE (parent_store))
285 dialog = e_alert_dialog_new_for_args (
286 parent, "mail:ask-delete-vfolder-nochild",
287 display_name, NULL);
288 else
289 dialog = e_alert_dialog_new_for_args (
290 parent, "mail:ask-delete-folder-nochild",
291 display_name, NULL);
292 }
293
294 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
295 EActivity *activity;
296 AsyncContext *context;
297 GCancellable *cancellable;
298
299 activity = e_mail_reader_new_activity (reader);
300 cancellable = e_activity_get_cancellable (activity);
301
302 context = g_slice_new0 (AsyncContext);
303 context->activity = activity;
304 context->reader = g_object_ref (reader);
305
306 /* Disable the dialog until the activity finishes. */
307 gtk_widget_set_sensitive (dialog, FALSE);
308
309 /* Destroy the dialog once the activity finishes. */
310 g_object_set_data_full (
311 G_OBJECT (activity), "delete-dialog",
312 dialog, (GDestroyNotify) gtk_widget_destroy);
313
314 e_mail_folder_remove (
315 folder, G_PRIORITY_DEFAULT,
316 cancellable, (GAsyncReadyCallback)
317 mail_reader_delete_folder_cb, context);
318 } else
319 gtk_widget_destroy (dialog);
320 }
321
322 static void
323 mail_reader_delete_folder_name_cb (GObject *source_object,
324 GAsyncResult *result,
325 gpointer user_data)
326 {
327 CamelStore *store;
328 CamelFolder *folder;
329 AsyncContext *context;
330 EAlertSink *alert_sink;
331 GError *error = NULL;
332
333 store = CAMEL_STORE (source_object);
334 context = (AsyncContext *) user_data;
335
336 alert_sink = e_activity_get_alert_sink (context->activity);
337
338 /* XXX The returned CamelFolder is a borrowed reference. */
339 folder = camel_store_get_folder_finish (store, result, &error);
340
341 if (e_activity_handle_cancellation (context->activity, error)) {
342 g_error_free (error);
343
344 } else if (error != NULL) {
345 e_alert_submit (
346 alert_sink, "mail:no-delete-folder",
347 context->folder_name, error->message, NULL);
348 g_error_free (error);
349
350 } else {
351 e_activity_set_state (
352 context->activity, E_ACTIVITY_COMPLETED);
353 e_mail_reader_delete_folder (context->reader, folder);
354 }
355
356 async_context_free (context);
357 }
358
359 void
360 e_mail_reader_delete_folder_name (EMailReader *reader,
361 CamelStore *store,
362 const gchar *folder_name)
363 {
364 EActivity *activity;
365 AsyncContext *context;
366 GCancellable *cancellable;
367
368 g_return_if_fail (E_IS_MAIL_READER (reader));
369 g_return_if_fail (CAMEL_IS_STORE (store));
370 g_return_if_fail (folder_name != NULL);
371
372 activity = e_mail_reader_new_activity (reader);
373 cancellable = e_activity_get_cancellable (activity);
374
375 context = g_slice_new0 (AsyncContext);
376 context->activity = activity;
377 context->reader = g_object_ref (reader);
378 context->folder_name = g_strdup (folder_name);
379
380 camel_store_get_folder (
381 store, folder_name,
382 CAMEL_STORE_FOLDER_INFO_FAST,
383 G_PRIORITY_DEFAULT, cancellable,
384 mail_reader_delete_folder_name_cb,
385 context);
386 }
387
388 /* Helper for e_mail_reader_expunge_folder() */
389 static void
390 mail_reader_expunge_folder_cb (GObject *source_object,
391 GAsyncResult *result,
392 gpointer user_data)
393 {
394 CamelFolder *folder;
395 AsyncContext *context;
396 EAlertSink *alert_sink;
397 GError *error = NULL;
398
399 folder = CAMEL_FOLDER (source_object);
400 context = (AsyncContext *) user_data;
401
402 alert_sink = e_activity_get_alert_sink (context->activity);
403
404 if (e_activity_handle_cancellation (context->activity, error)) {
405 g_error_free (error);
406
407 } else if (error != NULL) {
408 e_alert_submit (
409 alert_sink, "mail:no-expunge-folder",
410 camel_folder_get_display_name (folder),
411 error->message, NULL);
412 g_error_free (error);
413
414 } else {
415 e_activity_set_state (
416 context->activity, E_ACTIVITY_COMPLETED);
417 }
418
419 async_context_free (context);
420 }
421
422 void
423 e_mail_reader_expunge_folder (EMailReader *reader,
424 CamelFolder *folder)
425 {
426 GtkWindow *window;
427 const gchar *display_name;
428 gboolean proceed;
429
430 g_return_if_fail (E_IS_MAIL_READER (reader));
431 g_return_if_fail (CAMEL_IS_FOLDER (folder));
432
433 window = e_mail_reader_get_window (reader);
434 display_name = camel_folder_get_display_name (folder);
435
436 proceed = em_utils_prompt_user (
437 window, "prompt-on-expunge",
438 "mail:ask-expunge", display_name, NULL);
439
440 if (proceed) {
441 EActivity *activity;
442 AsyncContext *context;
443 GCancellable *cancellable;
444
445 activity = e_mail_reader_new_activity (reader);
446 cancellable = e_activity_get_cancellable (activity);
447
448 context = g_slice_new0 (AsyncContext);
449 context->activity = activity;
450 context->reader = g_object_ref (reader);
451
452 e_mail_folder_expunge (
453 folder, G_PRIORITY_DEFAULT, cancellable,
454 mail_reader_expunge_folder_cb, context);
455 }
456 }
457
458 /* Helper for e_mail_reader_expunge_folder_name() */
459 static void
460 mail_reader_expunge_folder_name_cb (GObject *source_object,
461 GAsyncResult *result,
462 gpointer user_data)
463 {
464 CamelStore *store;
465 CamelFolder *folder;
466 AsyncContext *context;
467 EAlertSink *alert_sink;
468 GError *error = NULL;
469
470 store = CAMEL_STORE (source_object);
471 context = (AsyncContext *) user_data;
472
473 alert_sink = e_activity_get_alert_sink (context->activity);
474
475 /* XXX The returned CamelFolder is a borrowed reference. */
476 folder = camel_store_get_folder_finish (store, result, &error);
477
478 if (e_activity_handle_cancellation (context->activity, error)) {
479 g_error_free (error);
480
481 } else if (error != NULL) {
482 e_alert_submit (
483 alert_sink, "mail:no-expunge-folder",
484 context->folder_name, error->message, NULL);
485 g_error_free (error);
486
487 } else {
488 e_activity_set_state (
489 context->activity, E_ACTIVITY_COMPLETED);
490 e_mail_reader_expunge_folder (context->reader, folder);
491 }
492
493 async_context_free (context);
494 }
495
496 void
497 e_mail_reader_expunge_folder_name (EMailReader *reader,
498 CamelStore *store,
499 const gchar *folder_name)
500 {
501 EActivity *activity;
502 AsyncContext *context;
503 GCancellable *cancellable;
504
505 g_return_if_fail (E_IS_MAIL_READER (reader));
506 g_return_if_fail (CAMEL_IS_STORE (store));
507 g_return_if_fail (folder_name != NULL);
508
509 activity = e_mail_reader_new_activity (reader);
510 cancellable = e_activity_get_cancellable (activity);
511
512 context = g_slice_new0 (AsyncContext);
513 context->activity = activity;
514 context->reader = g_object_ref (reader);
515 context->folder_name = g_strdup (folder_name);
516
517 camel_store_get_folder (
518 store, folder_name,
519 CAMEL_STORE_FOLDER_INFO_FAST,
520 G_PRIORITY_DEFAULT, cancellable,
521 mail_reader_expunge_folder_name_cb,
522 context);
523 }
524
525 /* Helper for e_mail_reader_refresh_folder() */
526 static void
527 mail_reader_refresh_folder_cb (GObject *source_object,
528 GAsyncResult *result,
529 gpointer user_data)
530 {
531 CamelFolder *folder;
532 AsyncContext *context;
533 EAlertSink *alert_sink;
534 GError *error = NULL;
535
536 folder = CAMEL_FOLDER (source_object);
537 context = (AsyncContext *) user_data;
538
539 alert_sink = e_activity_get_alert_sink (context->activity);
540
541 if (e_activity_handle_cancellation (context->activity, error)) {
542 g_error_free (error);
543
544 } else if (error != NULL) {
545 e_alert_submit (
546 alert_sink, "mail:no-refresh-folder",
547 camel_folder_get_display_name (folder),
548 error->message, NULL);
549 g_error_free (error);
550
551 } else {
552 e_activity_set_state (
553 context->activity, E_ACTIVITY_COMPLETED);
554 }
555
556 async_context_free (context);
557 }
558
559 void
560 e_mail_reader_refresh_folder (EMailReader *reader,
561 CamelFolder *folder)
562 {
563 EActivity *activity;
564 AsyncContext *context;
565 GCancellable *cancellable;
566
567 g_return_if_fail (E_IS_MAIL_READER (reader));
568 g_return_if_fail (CAMEL_IS_FOLDER (folder));
569
570 activity = e_mail_reader_new_activity (reader);
571 cancellable = e_activity_get_cancellable (activity);
572
573 context = g_slice_new0 (AsyncContext);
574 context->activity = activity;
575 context->reader = g_object_ref (reader);
576
577 camel_folder_refresh_info (
578 folder, G_PRIORITY_DEFAULT, cancellable,
579 mail_reader_refresh_folder_cb, context);
580 }
581
582 /* Helper for e_mail_reader_refresh_folder_name() */
583 static void
584 mail_reader_refresh_folder_name_cb (GObject *source_object,
585 GAsyncResult *result,
586 gpointer user_data)
587 {
588 CamelStore *store;
589 CamelFolder *folder;
590 AsyncContext *context;
591 EAlertSink *alert_sink;
592 GError *error = NULL;
593
594 store = CAMEL_STORE (source_object);
595 context = (AsyncContext *) user_data;
596
597 alert_sink = e_activity_get_alert_sink (context->activity);
598
599 /* XXX The returned CamelFolder is a borrowed reference. */
600 folder = camel_store_get_folder_finish (store, result, &error);
601
602 if (e_activity_handle_cancellation (context->activity, error)) {
603 g_error_free (error);
604
605 } else if (error != NULL) {
606 e_alert_submit (
607 alert_sink, "mail:no-refresh-folder",
608 context->folder_name, error->message, NULL);
609 g_error_free (error);
610
611 } else {
612 e_activity_set_state (
613 context->activity, E_ACTIVITY_COMPLETED);
614 e_mail_reader_refresh_folder (context->reader, folder);
615 }
616
617 async_context_free (context);
618 }
619
620 void
621 e_mail_reader_refresh_folder_name (EMailReader *reader,
622 CamelStore *store,
623 const gchar *folder_name)
624 {
625 EActivity *activity;
626 AsyncContext *context;
627 GCancellable *cancellable;
628
629 g_return_if_fail (E_IS_MAIL_READER (reader));
630 g_return_if_fail (CAMEL_IS_STORE (store));
631 g_return_if_fail (folder_name != NULL);
632
633 activity = e_mail_reader_new_activity (reader);
634 cancellable = e_activity_get_cancellable (activity);
635
636 context = g_slice_new0 (AsyncContext);
637 context->activity = activity;
638 context->reader = g_object_ref (reader);
639 context->folder_name = g_strdup (folder_name);
640
641 camel_store_get_folder (
642 store, folder_name,
643 CAMEL_STORE_FOLDER_INFO_FAST,
644 G_PRIORITY_DEFAULT, cancellable,
645 mail_reader_refresh_folder_name_cb,
646 context);
647 }
648
649 /* Helper for e_mail_reader_unsubscribe_folder_name() */
650 static void
651 mail_reader_unsubscribe_folder_name_cb (GObject *source_object,
652 GAsyncResult *result,
653 gpointer user_data)
654 {
655 CamelSubscribable *subscribable;
656 AsyncContext *context;
657 EAlertSink *alert_sink;
658 GError *error = NULL;
659
660 subscribable = CAMEL_SUBSCRIBABLE (source_object);
661 context = (AsyncContext *) user_data;
662
663 alert_sink = e_activity_get_alert_sink (context->activity);
664
665 camel_subscribable_unsubscribe_folder_finish (
666 subscribable, result, &error);
667
668 if (e_activity_handle_cancellation (context->activity, error)) {
669 g_error_free (error);
670
671 } else if (error != NULL) {
672 e_alert_submit (
673 alert_sink, "mail:folder-unsubscribe",
674 context->folder_name, error->message, NULL);
675 g_error_free (error);
676
677 } else {
678 e_activity_set_state (
679 context->activity, E_ACTIVITY_COMPLETED);
680 }
681
682 async_context_free (context);
683 }
684
685 void
686 e_mail_reader_unsubscribe_folder_name (EMailReader *reader,
687 CamelStore *store,
688 const gchar *folder_name)
689 {
690 EActivity *activity;
691 AsyncContext *context;
692 GCancellable *cancellable;
693
694 g_return_if_fail (E_IS_MAIL_READER (reader));
695 g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (store));
696 g_return_if_fail (folder_name != NULL);
697
698 activity = e_mail_reader_new_activity (reader);
699 cancellable = e_activity_get_cancellable (activity);
700
701 context = g_slice_new0 (AsyncContext);
702 context->activity = activity;
703 context->reader = g_object_ref (reader);
704 context->folder_name = g_strdup (folder_name);
705
706 camel_subscribable_unsubscribe_folder (
707 CAMEL_SUBSCRIBABLE (store), folder_name,
708 G_PRIORITY_DEFAULT, cancellable,
709 mail_reader_unsubscribe_folder_name_cb,
710 context);
711 }
712
713 guint
714 e_mail_reader_mark_selected (EMailReader *reader,
715 guint32 mask,
716 guint32 set)
717 {
718 CamelFolder *folder;
719 GPtrArray *uids;
720 guint ii;
721
722 g_return_val_if_fail (E_IS_MAIL_READER (reader), 0);
723
724 folder = e_mail_reader_get_folder (reader);
725
726 if (folder == NULL)
727 return 0;
728
729 camel_folder_freeze (folder);
730 uids = e_mail_reader_get_selected_uids (reader);
731
732 for (ii = 0; ii < uids->len; ii++)
733 camel_folder_set_message_flags (
734 folder, uids->pdata[ii], mask, set);
735
736 em_utils_uids_free (uids);
737
738 camel_folder_thaw (folder);
739
740 return ii;
741 }
742 static void
743 copy_tree_state (EMailReader *src_reader,
744 EMailReader *des_reader)
745 {
746 GtkWidget *src_mlist, *des_mlist;
747 gchar *state;
748
749 g_return_if_fail (src_reader != NULL);
750 g_return_if_fail (des_reader != NULL);
751
752 src_mlist = e_mail_reader_get_message_list (src_reader);
753 if (!src_mlist)
754 return;
755
756 des_mlist = e_mail_reader_get_message_list (des_reader);
757 if (!des_mlist)
758 return;
759
760 state = e_tree_get_state (E_TREE (src_mlist));
761 if (state)
762 e_tree_set_state (E_TREE (des_mlist), state);
763 g_free (state);
764
765 message_list_set_search (MESSAGE_LIST (des_mlist), MESSAGE_LIST (src_mlist)->search);
766 }
767
768 guint
769 e_mail_reader_open_selected (EMailReader *reader)
770 {
771 EShell *shell;
772 EMailBackend *backend;
773 ESourceRegistry *registry;
774 CamelFolder *folder;
775 GtkWindow *window;
776 GPtrArray *views;
777 GPtrArray *uids;
778 guint ii;
779
780 g_return_val_if_fail (E_IS_MAIL_READER (reader), 0);
781
782 backend = e_mail_reader_get_backend (reader);
783 shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
784 registry = e_shell_get_registry (shell);
785
786 folder = e_mail_reader_get_folder (reader);
787 uids = e_mail_reader_get_selected_uids (reader);
788 window = e_mail_reader_get_window (reader);
789
790 if (!em_utils_ask_open_many (window, uids->len)) {
791 em_utils_uids_free (uids);
792 return 0;
793 }
794
795 if (em_utils_folder_is_drafts (registry, folder) ||
796 em_utils_folder_is_outbox (registry, folder) ||
797 em_utils_folder_is_templates (registry, folder)) {
798 em_utils_edit_messages (reader, folder, uids, TRUE);
799 return uids->len;
800 }
801
802 views = g_ptr_array_new ();
803
804 /* For vfolders we need to edit the original, not the vfolder copy. */
805 for (ii = 0; ii < uids->len; ii++) {
806 const gchar *uid = uids->pdata[ii];
807 CamelFolder *real_folder;
808 CamelMessageInfo *info;
809 gchar *real_uid;
810
811 if (!CAMEL_IS_VEE_FOLDER (folder)) {
812 g_ptr_array_add (views, g_strdup (uid));
813 continue;
814 }
815
816 info = camel_folder_get_message_info (folder, uid);
817 if (info == NULL)
818 continue;
819
820 real_folder = camel_vee_folder_get_location (
821 CAMEL_VEE_FOLDER (folder),
822 (CamelVeeMessageInfo *) info, &real_uid);
823
824 if (em_utils_folder_is_drafts (registry, real_folder) ||
825 em_utils_folder_is_outbox (registry, real_folder)) {
826 GPtrArray *edits;
827
828 edits = g_ptr_array_new ();
829 g_ptr_array_add (edits, real_uid);
830 em_utils_edit_messages (
831 reader, real_folder, edits, TRUE);
832 } else {
833 g_free (real_uid);
834 g_ptr_array_add (views, g_strdup (uid));
835 }
836
837 camel_folder_free_message_info (folder, info);
838 }
839
840 for (ii = 0; ii < views->len; ii++) {
841 const gchar *uid = views->pdata[ii];
842 GtkWidget *browser;
843 MessageList *ml;
844
845 browser = e_mail_browser_new (
846 backend, folder, uid,
847 E_MAIL_FORMATTER_MODE_NORMAL);
848
849 e_mail_reader_set_folder (E_MAIL_READER (browser), folder);
850 e_mail_reader_set_message (E_MAIL_READER (browser), uid);
851
852 ml = MESSAGE_LIST (e_mail_reader_get_message_list (
853 E_MAIL_READER (browser)));
854 message_list_freeze (ml);
855
856 copy_tree_state (reader, E_MAIL_READER (browser));
857 e_mail_reader_set_group_by_threads (
858 E_MAIL_READER (browser),
859 e_mail_reader_get_group_by_threads (reader));
860
861 message_list_thaw (ml);
862 gtk_widget_show (browser);
863 }
864
865 g_ptr_array_foreach (views, (GFunc) g_free, NULL);
866 g_ptr_array_free (views, TRUE);
867
868 em_utils_uids_free (uids);
869
870 return ii;
871 }
872
873 static gboolean
874 destroy_printing_activity (EActivity *activity)
875 {
876 g_object_unref (activity);
877
878 return FALSE;
879 }
880
881 static void
882 printing_done_cb (EMailPrinter *printer,
883 GtkPrintOperation *operation,
884 GtkPrintOperationResult result,
885 gpointer user_data)
886 {
887 EActivity *activity = user_data;
888
889 if (result == GTK_PRINT_OPERATION_RESULT_ERROR) {
890
891 EAlertSink *alert_sink;
892 GError *error = NULL;
893
894 alert_sink = e_activity_get_alert_sink (activity);
895 gtk_print_operation_get_error (operation, &error);
896
897 if (error != NULL) {
898 e_alert_submit (
899 alert_sink, "mail:printing-failed",
900 error->message, NULL);
901 g_error_free (error);
902 }
903
904 g_object_unref (activity);
905 g_object_unref (printer);
906 return;
907 }
908
909 /* Set activity as completed, and keep it displayed for a few seconds
910 * so that user can actually see the the printing was sucesfully finished. */
911 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
912
913 /* We can't destroy the printer and associated WebKitWebView directly from
914 * here, because this callback is a handler of a WebKit's signal. This
915 * will destroy the printer later, together with the activity */
916 g_object_set_data_full (
917 G_OBJECT (activity),
918 "printer", printer, (GDestroyNotify) g_object_unref);
919
920 g_timeout_add_seconds_full (
921 G_PRIORITY_DEFAULT, 3,
922 (GSourceFunc) destroy_printing_activity, activity, NULL);
923 }
924
925 struct _MessagePrintingContext {
926 EMailReader *reader;
927 CamelFolder *folder;
928 gchar *message_uid;
929 GtkPrintOperationAction action;
930
931 EActivity *activity;
932 };
933
934 static void
935 free_message_printing_context (struct _MessagePrintingContext *context)
936 {
937 g_return_if_fail (context != NULL);
938
939 g_clear_object (&context->reader);
940 g_clear_object (&context->folder);
941 g_clear_object (&context->activity);
942
943 if (context->message_uid)
944 g_free (context->message_uid);
945
946 g_free (context);
947 }
948
949 static void
950 mail_reader_do_print_message (GObject *object,
951 GAsyncResult *result,
952 gpointer user_data)
953 {
954 EMailReader *reader = E_MAIL_READER (object);
955 EMailDisplay *mail_display;
956 EActivity *activity;
957 GCancellable *cancellable;
958 EMailPrinter *printer;
959 EMailPartList *part_list;
960 struct _MessagePrintingContext *context = user_data;
961
962 activity = e_mail_reader_new_activity (context->reader);
963 e_activity_set_text (activity, _("Printing"));
964 e_activity_set_state (activity, E_ACTIVITY_RUNNING);
965 cancellable = e_activity_get_cancellable (activity);
966
967 part_list = e_mail_reader_parse_message_finish (reader, result);
968
969 printer = e_mail_printer_new (part_list);
970 g_signal_connect (
971 printer, "done",
972 G_CALLBACK (printing_done_cb), activity);
973
974 mail_display = e_mail_reader_get_mail_display (reader);
975
976 e_mail_printer_print (printer, context->action,
977 e_mail_display_get_formatter (mail_display),
978 cancellable);
979
980 free_message_printing_context (context);
981 }
982
983 static void
984 mail_reader_get_message_to_print_ready_cb (GObject *object,
985 GAsyncResult *result,
986 gpointer user_data)
987 {
988 CamelMimeMessage *message;
989 struct _MessagePrintingContext *context = user_data;
990
991 message = camel_folder_get_message_finish (CAMEL_FOLDER (object), result, NULL);
992 if (!CAMEL_IS_MIME_MESSAGE (message)) {
993 free_message_printing_context (context);
994 return;
995 }
996
997 /* "Retrieving message" activity (or NULL) */
998 g_clear_object (&context->activity);
999
1000 e_mail_reader_parse_message (
1001 context->reader, context->folder, context->message_uid,
1002 message, NULL, mail_reader_do_print_message, context);
1003 }
1004
1005 void
1006 e_mail_reader_print (EMailReader *reader,
1007 GtkPrintOperationAction action)
1008 {
1009 struct _MessagePrintingContext *context;
1010 MessageList *message_list;
1011
1012 context = g_new0 (struct _MessagePrintingContext, 1);
1013
1014 message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
1015 context->reader = g_object_ref (reader);
1016 context->message_uid = g_strdup (message_list->cursor_uid);
1017 context->folder = g_object_ref (e_mail_reader_get_folder (reader));
1018 context->activity = e_mail_reader_new_activity (reader);
1019 context->action = action;
1020
1021 g_return_if_fail (E_IS_MAIL_READER (reader));
1022
1023 camel_folder_get_message (
1024 context->folder, context->message_uid,
1025 G_PRIORITY_DEFAULT, e_activity_get_cancellable (context->activity),
1026 (GAsyncReadyCallback) mail_reader_get_message_to_print_ready_cb,
1027 context);
1028 }
1029
1030 static void
1031 mail_reader_remove_attachments_cb (CamelFolder *folder,
1032 GAsyncResult *result,
1033 AsyncContext *context)
1034 {
1035 EAlertSink *alert_sink;
1036 GError *error = NULL;
1037
1038 alert_sink = e_mail_reader_get_alert_sink (context->reader);
1039
1040 e_mail_folder_remove_attachments_finish (folder, result, &error);
1041
1042 if (e_activity_handle_cancellation (context->activity, error)) {
1043 g_error_free (error);
1044
1045 } else if (error != NULL) {
1046 e_alert_submit (
1047 alert_sink,
1048 "mail:remove-attachments",
1049 error->message, NULL);
1050 g_error_free (error);
1051 }
1052
1053 async_context_free (context);
1054 }
1055
1056 void
1057 e_mail_reader_remove_attachments (EMailReader *reader)
1058 {
1059 EActivity *activity;
1060 AsyncContext *context;
1061 GCancellable *cancellable;
1062 CamelFolder *folder;
1063 GPtrArray *uids;
1064
1065 g_return_if_fail (E_IS_MAIL_READER (reader));
1066
1067 folder = e_mail_reader_get_folder (reader);
1068 uids = e_mail_reader_get_selected_uids (reader);
1069 g_return_if_fail (uids != NULL);
1070
1071 /* XXX Either e_mail_reader_get_selected_uids()
1072 * or MessageList should do this itself. */
1073 g_ptr_array_set_free_func (uids, (GDestroyNotify) g_free);
1074
1075 /* Remove attachments asynchronously. */
1076
1077 activity = e_mail_reader_new_activity (reader);
1078 cancellable = e_activity_get_cancellable (activity);
1079
1080 context = g_slice_new0 (AsyncContext);
1081 context->activity = activity;
1082 context->reader = g_object_ref (reader);
1083
1084 e_mail_folder_remove_attachments (
1085 folder, uids, G_PRIORITY_DEFAULT,
1086 cancellable, (GAsyncReadyCallback)
1087 mail_reader_remove_attachments_cb,
1088 context);
1089
1090 g_ptr_array_unref (uids);
1091 }
1092
1093 static void
1094 mail_reader_remove_duplicates_cb (CamelFolder *folder,
1095 GAsyncResult *result,
1096 AsyncContext *context)
1097 {
1098 EAlertSink *alert_sink;
1099 GHashTable *duplicates;
1100 GtkWindow *parent_window;
1101 guint n_duplicates;
1102 GError *error = NULL;
1103
1104 alert_sink = e_mail_reader_get_alert_sink (context->reader);
1105 parent_window = e_mail_reader_get_window (context->reader);
1106
1107 duplicates = e_mail_folder_find_duplicate_messages_finish (
1108 folder, result, &error);
1109
1110 if (e_activity_handle_cancellation (context->activity, error)) {
1111 g_warn_if_fail (duplicates == NULL);
1112 async_context_free (context);
1113 g_error_free (error);
1114 return;
1115
1116 } else if (error != NULL) {
1117 g_warn_if_fail (duplicates == NULL);
1118 e_alert_submit (
1119 alert_sink,
1120 "mail:find-duplicate-messages",
1121 error->message, NULL);
1122 async_context_free (context);
1123 g_error_free (error);
1124 return;
1125 }
1126
1127 g_return_if_fail (duplicates != NULL);
1128
1129 /* Finalize the activity here so we don't leave a message in
1130 * the task bar while prompting the user for confirmation. */
1131 e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
1132 g_object_unref (context->activity);
1133 context->activity = NULL;
1134
1135 n_duplicates = g_hash_table_size (duplicates);
1136
1137 if (n_duplicates == 0) {
1138 em_utils_prompt_user (
1139 parent_window, NULL,
1140 "mail:info-no-remove-duplicates",
1141 camel_folder_get_display_name (folder), NULL);
1142 } else {
1143 gchar *confirmation;
1144 gboolean proceed;
1145
1146 confirmation = g_strdup_printf (ngettext (
1147 /* Translators: %s is replaced with a folder
1148 * name %u with count of duplicate messages. */
1149 "Folder '%s' contains %u duplicate message. "
1150 "Are you sure you want to delete it?",
1151 "Folder '%s' contains %u duplicate messages. "
1152 "Are you sure you want to delete them?",
1153 n_duplicates),
1154 camel_folder_get_display_name (folder),
1155 n_duplicates);
1156
1157 proceed = em_utils_prompt_user (
1158 parent_window, NULL,
1159 "mail:ask-remove-duplicates",
1160 confirmation, NULL);
1161
1162 if (proceed) {
1163 GHashTableIter iter;
1164 gpointer key;
1165
1166 camel_folder_freeze (folder);
1167
1168 g_hash_table_iter_init (&iter, duplicates);
1169
1170 /* Mark duplicate messages for deletion. */
1171 while (g_hash_table_iter_next (&iter, &key, NULL))
1172 camel_folder_delete_message (folder, key);
1173
1174 camel_folder_thaw (folder);
1175 }
1176
1177 g_free (confirmation);
1178 }
1179
1180 g_hash_table_destroy (duplicates);
1181
1182 async_context_free (context);
1183 }
1184
1185 void
1186 e_mail_reader_remove_duplicates (EMailReader *reader)
1187 {
1188 EActivity *activity;
1189 AsyncContext *context;
1190 GCancellable *cancellable;
1191 CamelFolder *folder;
1192 GPtrArray *uids;
1193
1194 g_return_if_fail (E_IS_MAIL_READER (reader));
1195
1196 folder = e_mail_reader_get_folder (reader);
1197 uids = e_mail_reader_get_selected_uids (reader);
1198 g_return_if_fail (uids != NULL);
1199
1200 /* XXX Either e_mail_reader_get_selected_uids()
1201 * or MessageList should do this itself. */
1202 g_ptr_array_set_free_func (uids, (GDestroyNotify) g_free);
1203
1204 /* Find duplicate messages asynchronously. */
1205
1206 activity = e_mail_reader_new_activity (reader);
1207 cancellable = e_activity_get_cancellable (activity);
1208
1209 context = g_slice_new0 (AsyncContext);
1210 context->activity = activity;
1211 context->reader = g_object_ref (reader);
1212
1213 e_mail_folder_find_duplicate_messages (
1214 folder, uids, G_PRIORITY_DEFAULT,
1215 cancellable, (GAsyncReadyCallback)
1216 mail_reader_remove_duplicates_cb,
1217 context);
1218
1219 g_ptr_array_unref (uids);
1220 }
1221
1222 /* Helper for e_mail_reader_reply_to_message()
1223 * XXX This function belongs in e-html-utils.c */
1224 static gboolean
1225 html_contains_nonwhitespace (const gchar *html,
1226 gint len)
1227 {
1228 const gchar *cp;
1229 gunichar uc = 0;
1230
1231 if (html == NULL || len <= 0)
1232 return FALSE;
1233
1234 cp = html;
1235
1236 while (cp != NULL && cp - html < len) {
1237 uc = g_utf8_get_char (cp);
1238 if (uc == 0)
1239 break;
1240
1241 if (uc == '<') {
1242 /* skip until next '>' */
1243 uc = g_utf8_get_char (cp);
1244 while (uc != 0 && uc != '>' && cp - html < len) {
1245 cp = g_utf8_next_char (cp);
1246 uc = g_utf8_get_char (cp);
1247 }
1248 if (uc == 0)
1249 break;
1250 } else if (uc == '&') {
1251 /* sequence ' ' is a space */
1252 if (g_ascii_strncasecmp (cp, " ", 6) == 0)
1253 cp = cp + 5;
1254 else
1255 break;
1256 } else if (!g_unichar_isspace (uc))
1257 break;
1258
1259 cp = g_utf8_next_char (cp);
1260 }
1261
1262 return cp - html < len - 1 && uc != 0;
1263 }
1264
1265 static void
1266 mail_reader_reply_message_parsed (GObject *object,
1267 GAsyncResult *result,
1268 gpointer user_data)
1269 {
1270 EShell *shell;
1271 EMailBackend *backend;
1272 EMailReader *reader = E_MAIL_READER (object);
1273 EMailPartList *part_list;
1274 AsyncContext *context = user_data;
1275
1276 part_list = e_mail_reader_parse_message_finish (reader, result);
1277
1278 backend = e_mail_reader_get_backend (context->reader);
1279 shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
1280
1281 em_utils_reply_to_message (
1282 shell, part_list->message,
1283 context->folder, context->message_uid,
1284 context->reply_type, context->reply_style,
1285 part_list, context->address);
1286
1287 g_object_unref (part_list);
1288 async_context_free (context);
1289 }
1290
1291 static void
1292 mail_reader_get_message_ready_cb (CamelFolder *folder,
1293 GAsyncResult *result,
1294 AsyncContext *context)
1295 {
1296 EAlertSink *alert_sink;
1297 CamelMimeMessage *message;
1298 GError *error = NULL;
1299
1300 alert_sink = e_mail_reader_get_alert_sink (context->reader);
1301
1302 message = camel_folder_get_message_finish (folder, result, &error);
1303
1304 if (e_activity_handle_cancellation (context->activity, error)) {
1305 g_warn_if_fail (message == NULL);
1306 async_context_free (context);
1307 g_error_free (error);
1308 return;
1309
1310 } else if (error != NULL) {
1311 g_warn_if_fail (message == NULL);
1312 e_alert_submit (
1313 alert_sink, "mail:no-retrieve-message",
1314 error->message, NULL);
1315 async_context_free (context);
1316 g_error_free (error);
1317 return;
1318 }
1319
1320 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1321
1322 e_mail_reader_parse_message (
1323 context->reader, context->folder,
1324 context->message_uid, message, NULL,
1325 mail_reader_reply_message_parsed, context);
1326 }
1327
1328 void
1329 e_mail_reader_reply_to_message (EMailReader *reader,
1330 CamelMimeMessage *src_message,
1331 EMailReplyType reply_type)
1332 {
1333 EShell *shell;
1334 EMailBackend *backend;
1335 EShellBackend *shell_backend;
1336 EMailDisplay *display;
1337 EMailPartList *part_list = NULL;
1338 GtkWidget *message_list;
1339 CamelMimeMessage *new_message;
1340 CamelInternetAddress *address = NULL;
1341 CamelFolder *folder;
1342 EMailReplyStyle reply_style;
1343 EWebView *web_view;
1344 struct _camel_header_raw *header;
1345 const gchar *uid;
1346 gchar *selection = NULL;
1347 gint length;
1348 gchar *mail_uri;
1349 CamelObjectBag *registry;
1350
1351 /* This handles quoting only selected text in the reply. If
1352 * nothing is selected or only whitespace is selected, fall
1353 * back to the normal em_utils_reply_to_message(). */
1354
1355 g_return_if_fail (E_IS_MAIL_READER (reader));
1356
1357 backend = e_mail_reader_get_backend (reader);
1358 folder = e_mail_reader_get_folder (reader);
1359 display = e_mail_reader_get_mail_display (reader);
1360 message_list = e_mail_reader_get_message_list (reader);
1361 reply_style = e_mail_reader_get_reply_style (reader);
1362
1363 shell_backend = E_SHELL_BACKEND (backend);
1364 shell = e_shell_backend_get_shell (shell_backend);
1365
1366 web_view = E_WEB_VIEW (display);
1367
1368 if (reply_type == E_MAIL_REPLY_TO_RECIPIENT) {
1369 const gchar *uri;
1370
1371 uri = e_web_view_get_selected_uri (web_view);
1372
1373 if (uri) {
1374 CamelURL *curl;
1375
1376 curl = camel_url_new (uri, NULL);
1377
1378 if (curl && curl->path && *curl->path) {
1379 address = camel_internet_address_new ();
1380 if (camel_address_decode (
1381 CAMEL_ADDRESS (address),
1382 curl->path) < 0) {
1383 g_object_unref (address);
1384 address = NULL;
1385 }
1386 }
1387
1388 if (curl)
1389 camel_url_free (curl);
1390 }
1391 }
1392
1393 uid = MESSAGE_LIST (message_list)->cursor_uid;
1394 g_return_if_fail (uid != NULL);
1395
1396 if (!gtk_widget_get_visible (GTK_WIDGET (web_view)))
1397 goto whole_message;
1398
1399 registry = e_mail_part_list_get_registry ();
1400 mail_uri = e_mail_part_build_uri (folder, uid, NULL, NULL);
1401 part_list = camel_object_bag_get (registry, mail_uri);
1402 g_free (mail_uri);
1403
1404 if (!part_list)
1405 goto whole_message;
1406
1407 if (src_message == NULL) {
1408 src_message = part_list->message;
1409 if (src_message != NULL)
1410 g_object_ref (src_message);
1411
1412 g_object_unref (part_list);
1413 part_list = NULL;
1414
1415 g_return_if_fail (src_message != NULL);
1416 } else {
1417 g_object_unref (part_list);
1418 part_list = NULL;
1419 }
1420
1421 if (!e_web_view_is_selection_active (web_view))
1422 goto whole_message;
1423
1424 selection = e_web_view_get_selection_html (web_view);
1425 if (selection == NULL || *selection == '\0')
1426 goto whole_message;
1427
1428 length = strlen (selection);
1429 if (!html_contains_nonwhitespace (selection, length))
1430 goto whole_message;
1431
1432 new_message = camel_mime_message_new ();
1433
1434 /* Filter out "content-*" headers. */
1435 header = CAMEL_MIME_PART (src_message)->headers;
1436 while (header != NULL) {
1437 if (g_ascii_strncasecmp (header->name, "content-", 8) != 0)
1438 camel_medium_add_header (
1439 CAMEL_MEDIUM (new_message),
1440 header->name, header->value);
1441
1442 header = header->next;
1443 }
1444
1445 camel_mime_part_set_encoding (
1446 CAMEL_MIME_PART (new_message),
1447 CAMEL_TRANSFER_ENCODING_8BIT);
1448
1449 camel_mime_part_set_content (
1450 CAMEL_MIME_PART (new_message),
1451 selection, length, "text/html");
1452
1453 g_object_unref (src_message);
1454
1455 em_utils_reply_to_message (
1456 shell, new_message, folder, uid,
1457 reply_type, reply_style, NULL, address);
1458
1459 if (address)
1460 g_object_unref (address);
1461
1462 g_object_unref (new_message);
1463
1464 g_free (selection);
1465
1466 return;
1467
1468 whole_message:
1469 if (src_message == NULL) {
1470 EActivity *activity;
1471 AsyncContext *context;
1472 GCancellable *cancellable;
1473
1474 activity = e_mail_reader_new_activity (reader);
1475 cancellable = e_activity_get_cancellable (activity);
1476
1477 context = g_slice_new0 (AsyncContext);
1478 context->activity = activity;
1479 context->folder = g_object_ref (folder);
1480 context->reader = g_object_ref (reader);
1481 context->address = address; /* takes ownership of it, if set */
1482 context->message_uid = g_strdup (uid);
1483 context->reply_type = reply_type;
1484 context->reply_style = reply_style;
1485
1486 camel_folder_get_message (
1487 context->folder, context->message_uid,
1488 G_PRIORITY_DEFAULT, cancellable,
1489 (GAsyncReadyCallback) mail_reader_get_message_ready_cb,
1490 context);
1491
1492 return;
1493 }
1494
1495 em_utils_reply_to_message (
1496 shell, src_message, folder, uid,
1497 reply_type, reply_style, part_list, address);
1498
1499 if (address)
1500 g_object_unref (address);
1501 }
1502
1503 static void
1504 mail_reader_save_messages_cb (CamelFolder *folder,
1505 GAsyncResult *result,
1506 AsyncContext *context)
1507 {
1508 EAlertSink *alert_sink;
1509 GError *error = NULL;
1510
1511 alert_sink = e_mail_reader_get_alert_sink (context->reader);
1512
1513 e_mail_folder_save_messages_finish (folder, result, &error);
1514
1515 if (e_activity_handle_cancellation (context->activity, error)) {
1516 g_error_free (error);
1517
1518 } else if (error != NULL) {
1519 e_alert_submit (
1520 alert_sink,
1521 "mail:save-messages",
1522 error->message, NULL);
1523 g_error_free (error);
1524 }
1525
1526 async_context_free (context);
1527 }
1528
1529 void
1530 e_mail_reader_save_messages (EMailReader *reader)
1531 {
1532 EShell *shell;
1533 EActivity *activity;
1534 AsyncContext *context;
1535 EMailBackend *backend;
1536 GCancellable *cancellable;
1537 EShellBackend *shell_backend;
1538 CamelMessageInfo *info;
1539 CamelFolder *folder;
1540 GFile *destination;
1541 GPtrArray *uids;
1542 const gchar *message_uid;
1543 const gchar *title;
1544 gchar *suggestion = NULL;
1545
1546 folder = e_mail_reader_get_folder (reader);
1547 backend = e_mail_reader_get_backend (reader);
1548
1549 uids = e_mail_reader_get_selected_uids (reader);
1550 g_return_if_fail (uids != NULL && uids->len > 0);
1551 message_uid = g_ptr_array_index (uids, 0);
1552
1553 /* XXX Either e_mail_reader_get_selected_uids()
1554 * or MessageList should do this itself. */
1555 g_ptr_array_set_free_func (uids, (GDestroyNotify) g_free);
1556
1557 title = ngettext ("Save Message", "Save Messages", uids->len);
1558
1559 /* Suggest as a filename the subject of the first message. */
1560 info = camel_folder_get_message_info (folder, message_uid);
1561 if (info != NULL) {
1562 const gchar *subject;
1563
1564 subject = camel_message_info_subject (info);
1565 if (subject != NULL)
1566 suggestion = g_strconcat (subject, ".mbox", NULL);
1567 camel_folder_free_message_info (folder, info);
1568 }
1569
1570 if (suggestion == NULL) {
1571 const gchar *basename;
1572
1573 /* Translators: This is part of a suggested file name
1574 * used when saving a message or multiple messages to
1575 * mbox format, when the first message doesn't have a
1576 * subject. The extension ".mbox" is appended to the
1577 * string; for example "Message.mbox". */
1578 basename = ngettext ("Message", "Messages", uids->len);
1579 suggestion = g_strconcat (basename, ".mbox", NULL);
1580 }
1581
1582 shell_backend = E_SHELL_BACKEND (backend);
1583 shell = e_shell_backend_get_shell (shell_backend);
1584
1585 destination = e_shell_run_save_dialog (
1586 shell, title, suggestion,
1587 "*.mbox:application/mbox,message/rfc822", NULL, NULL);
1588
1589 if (destination == NULL) {
1590 g_ptr_array_unref (uids);
1591 return;
1592 }
1593
1594 /* Save messages asynchronously. */
1595
1596 activity = e_mail_reader_new_activity (reader);
1597 cancellable = e_activity_get_cancellable (activity);
1598
1599 context = g_slice_new0 (AsyncContext);
1600 context->activity = activity;
1601 context->reader = g_object_ref (reader);
1602
1603 e_mail_folder_save_messages (
1604 folder, uids,
1605 destination, G_PRIORITY_DEFAULT,
1606 cancellable, (GAsyncReadyCallback)
1607 mail_reader_save_messages_cb, context);
1608
1609 g_object_unref (destination);
1610 g_ptr_array_unref (uids);
1611 }
1612
1613 void
1614 e_mail_reader_select_next_message (EMailReader *reader,
1615 gboolean or_else_previous)
1616 {
1617 GtkWidget *message_list;
1618 gboolean hide_deleted;
1619 gboolean success;
1620
1621 g_return_if_fail (E_IS_MAIL_READER (reader));
1622
1623 hide_deleted = e_mail_reader_get_hide_deleted (reader);
1624 message_list = e_mail_reader_get_message_list (reader);
1625
1626 success = message_list_select (
1627 MESSAGE_LIST (message_list),
1628 MESSAGE_LIST_SELECT_NEXT, 0, 0);
1629
1630 if (!success && (hide_deleted || or_else_previous))
1631 message_list_select (
1632 MESSAGE_LIST (message_list),
1633 MESSAGE_LIST_SELECT_PREVIOUS, 0, 0);
1634 }
1635
1636 /* Helper for e_mail_reader_create_filter_from_selected() */
1637 static void
1638 mail_reader_create_filter_cb (CamelFolder *folder,
1639 GAsyncResult *result,
1640 AsyncContext *context)
1641 {
1642 EMailBackend *backend;
1643 EMailSession *session;
1644 EAlertSink *alert_sink;
1645 CamelMimeMessage *message;
1646 GError *error = NULL;
1647
1648 alert_sink = e_activity_get_alert_sink (context->activity);
1649
1650 message = camel_folder_get_message_finish (folder, result, &error);
1651
1652 if (e_activity_handle_cancellation (context->activity, error)) {
1653 g_warn_if_fail (message == NULL);
1654 async_context_free (context);
1655 g_error_free (error);
1656 return;
1657
1658 } else if (error != NULL) {
1659 g_warn_if_fail (message == NULL);
1660 e_alert_submit (
1661 alert_sink, "mail:no-retrieve-message",
1662 error->message, NULL);
1663 async_context_free (context);
1664 g_error_free (error);
1665 return;
1666 }
1667
1668 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1669
1670 /* Finalize the activity here so we don't leave a message
1671 * in the task bar while displaying the filter editor. */
1672 e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
1673 g_object_unref (context->activity);
1674 context->activity = NULL;
1675
1676 backend = e_mail_reader_get_backend (context->reader);
1677 session = e_mail_backend_get_session (backend);
1678
1679 filter_gui_add_from_message (
1680 session, message,
1681 context->filter_source,
1682 context->filter_type);
1683
1684 g_object_unref (message);
1685
1686 async_context_free (context);
1687 }
1688
1689 void
1690 e_mail_reader_create_filter_from_selected (EMailReader *reader,
1691 gint filter_type)
1692 {
1693 EShell *shell;
1694 EActivity *activity;
1695 EMailBackend *backend;
1696 AsyncContext *context;
1697 GCancellable *cancellable;
1698 ESourceRegistry *registry;
1699 CamelFolder *folder;
1700 GPtrArray *uids;
1701 const gchar *filter_source;
1702 const gchar *message_uid;
1703
1704 g_return_if_fail (E_IS_MAIL_READER (reader));
1705
1706 backend = e_mail_reader_get_backend (reader);
1707 shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
1708 registry = e_shell_get_registry (shell);
1709
1710 folder = e_mail_reader_get_folder (reader);
1711 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1712
1713 if (em_utils_folder_is_sent (registry, folder))
1714 filter_source = E_FILTER_SOURCE_OUTGOING;
1715 else if (em_utils_folder_is_outbox (registry, folder))
1716 filter_source = E_FILTER_SOURCE_OUTGOING;
1717 else
1718 filter_source = E_FILTER_SOURCE_INCOMING;
1719
1720 uids = e_mail_reader_get_selected_uids (reader);
1721 g_return_if_fail (uids != NULL && uids->len == 1);
1722 message_uid = g_ptr_array_index (uids, 0);
1723
1724 activity = e_mail_reader_new_activity (reader);
1725 cancellable = e_activity_get_cancellable (activity);
1726
1727 context = g_slice_new0 (AsyncContext);
1728 context->activity = activity;
1729 context->reader = g_object_ref (reader);
1730 context->filter_source = filter_source;
1731 context->filter_type = filter_type;
1732
1733 camel_folder_get_message (
1734 folder, message_uid, G_PRIORITY_DEFAULT,
1735 cancellable, (GAsyncReadyCallback)
1736 mail_reader_create_filter_cb, context);
1737
1738 em_utils_uids_free (uids);
1739 }
1740
1741 /* Helper for e_mail_reader_create_vfolder_from_selected() */
1742 static void
1743 mail_reader_create_vfolder_cb (CamelFolder *folder,
1744 GAsyncResult *result,
1745 AsyncContext *context)
1746 {
1747 EMailBackend *backend;
1748 EMailSession *session;
1749 EAlertSink *alert_sink;
1750 CamelMimeMessage *message;
1751 CamelFolder *use_folder;
1752 GError *error = NULL;
1753
1754 alert_sink = e_activity_get_alert_sink (context->activity);
1755
1756 message = camel_folder_get_message_finish (folder, result, &error);
1757
1758 if (e_activity_handle_cancellation (context->activity, error)) {
1759 g_warn_if_fail (message == NULL);
1760 async_context_free (context);
1761 g_error_free (error);
1762 return;
1763
1764 } else if (error != NULL) {
1765 g_warn_if_fail (message == NULL);
1766 e_alert_submit (
1767 alert_sink, "mail:no-retrieve-message",
1768 error->message, NULL);
1769 async_context_free (context);
1770 g_error_free (error);
1771 return;
1772 }
1773
1774 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1775
1776 /* Finalize the activity here so we don't leave a message
1777 * in the task bar while displaying the vfolder editor. */
1778 e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
1779 g_object_unref (context->activity);
1780 context->activity = NULL;
1781
1782 backend = e_mail_reader_get_backend (context->reader);
1783 session = e_mail_backend_get_session (backend);
1784
1785 use_folder = context->folder;
1786 if (CAMEL_IS_VEE_FOLDER (use_folder)) {
1787 CamelStore *parent_store;
1788 CamelVeeFolder *vfolder;
1789
1790 parent_store = camel_folder_get_parent_store (use_folder);
1791 vfolder = CAMEL_VEE_FOLDER (use_folder);
1792
1793 if (CAMEL_IS_VEE_STORE (parent_store) &&
1794 vfolder == camel_vee_store_get_unmatched_folder (CAMEL_VEE_STORE (parent_store))) {
1795 /* use source folder instead of the Unmatched folder */
1796 use_folder = camel_vee_folder_get_vee_uid_folder (
1797 vfolder, context->message_uid);
1798 }
1799 }
1800
1801 vfolder_gui_add_from_message (
1802 session, message,
1803 context->filter_type,
1804 use_folder);
1805
1806 g_object_unref (message);
1807
1808 async_context_free (context);
1809 }
1810
1811 void
1812 e_mail_reader_create_vfolder_from_selected (EMailReader *reader,
1813 gint vfolder_type)
1814 {
1815 EActivity *activity;
1816 AsyncContext *context;
1817 GCancellable *cancellable;
1818 CamelFolder *folder;
1819 GPtrArray *uids;
1820 const gchar *message_uid;
1821
1822 g_return_if_fail (E_IS_MAIL_READER (reader));
1823
1824 folder = e_mail_reader_get_folder (reader);
1825 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1826
1827 uids = e_mail_reader_get_selected_uids (reader);
1828 g_return_if_fail (uids != NULL && uids->len == 1);
1829 message_uid = g_ptr_array_index (uids, 0);
1830
1831 activity = e_mail_reader_new_activity (reader);
1832 cancellable = e_activity_get_cancellable (activity);
1833
1834 context = g_slice_new0 (AsyncContext);
1835 context->activity = activity;
1836 context->folder = g_object_ref (folder);
1837 context->reader = g_object_ref (reader);
1838 context->message_uid = g_strdup (message_uid);
1839 context->filter_type = vfolder_type;
1840
1841 camel_folder_get_message (
1842 folder, message_uid, G_PRIORITY_DEFAULT,
1843 cancellable, (GAsyncReadyCallback)
1844 mail_reader_create_vfolder_cb, context);
1845
1846 em_utils_uids_free (uids);
1847 }
1848
1849 static EMailReaderHeader *
1850 emr_header_from_xmldoc (xmlDocPtr doc)
1851 {
1852 EMailReaderHeader *h;
1853 xmlNodePtr root;
1854 xmlChar *name;
1855
1856 if (doc == NULL)
1857 return NULL;
1858
1859 root = doc->children;
1860 if (strcmp ((gchar *) root->name, "header") != 0)
1861 return NULL;
1862
1863 name = xmlGetProp (root, (const guchar *)"name");
1864 if (name == NULL)
1865 return NULL;
1866
1867 h = g_malloc0 (sizeof (EMailReaderHeader));
1868 h->name = g_strdup ((gchar *) name);
1869 xmlFree (name);
1870
1871 if (xmlHasProp (root, (const guchar *)"enabled"))
1872 h->enabled = 1;
1873 else
1874 h->enabled = 0;
1875
1876 return h;
1877 }
1878
1879 /**
1880 * e_mail_reader_header_from_xml
1881 * @xml: XML configuration data
1882 *
1883 * Parses passed XML data, which should be of
1884 * the format <header name="foo" enabled />, and
1885 * returns a EMailReaderHeader structure, or NULL if there
1886 * is an error.
1887 **/
1888 EMailReaderHeader *
1889 e_mail_reader_header_from_xml (const gchar *xml)
1890 {
1891 EMailReaderHeader *header;
1892 xmlDocPtr doc;
1893
1894 if (!(doc = xmlParseDoc ((guchar *) xml)))
1895 return NULL;
1896
1897 header = emr_header_from_xmldoc (doc);
1898 xmlFreeDoc (doc);
1899
1900 return header;
1901 }
1902
1903 /**
1904 * e_mail_reader_header_to_xml
1905 * @header: header from which to generate XML
1906 *
1907 * Returns the passed header as a XML structure,
1908 * or NULL on error
1909 */
1910 gchar *
1911 e_mail_reader_header_to_xml (EMailReaderHeader *header)
1912 {
1913 xmlDocPtr doc;
1914 xmlNodePtr root;
1915 xmlChar *xml;
1916 gchar *out;
1917 gint size;
1918
1919 g_return_val_if_fail (header != NULL, NULL);
1920 g_return_val_if_fail (header->name != NULL, NULL);
1921
1922 doc = xmlNewDoc ((const guchar *)"1.0");
1923
1924 root = xmlNewDocNode (doc, NULL, (const guchar *)"header", NULL);
1925 xmlSetProp (root, (const guchar *)"name", (guchar *) header->name);
1926 if (header->enabled)
1927 xmlSetProp (root, (const guchar *)"enabled", NULL);
1928
1929 xmlDocSetRootElement (doc, root);
1930 xmlDocDumpMemory (doc, &xml, &size);
1931 xmlFreeDoc (doc);
1932
1933 out = g_malloc (size + 1);
1934 memcpy (out, xml, size);
1935 out[size] = '\0';
1936 xmlFree (xml);
1937
1938 return out;
1939 }
1940
1941 /**
1942 * e_mail_reader_header_free
1943 * @header: header to free
1944 *
1945 * Frees the memory associated with the passed header
1946 * structure.
1947 */
1948 void
1949 e_mail_reader_header_free (EMailReaderHeader *header)
1950 {
1951 if (header == NULL)
1952 return;
1953
1954 g_free (header->name);
1955 g_free (header);
1956 }
1957
1958 static void
1959 mail_reader_parse_message_run (GSimpleAsyncResult *simple,
1960 GObject *object,
1961 GCancellable *cancellable)
1962 {
1963 EMailReader *reader = E_MAIL_READER (object);
1964 CamelObjectBag *registry;
1965 EMailPartList *part_list;
1966 AsyncContext *async_context;
1967 gchar *mail_uri;
1968
1969 async_context = g_simple_async_result_get_op_res_gpointer (simple);
1970
1971 registry = e_mail_part_list_get_registry ();
1972
1973 mail_uri = e_mail_part_build_uri (
1974 async_context->folder,
1975 async_context->message_uid, NULL, NULL);
1976
1977 part_list = camel_object_bag_reserve (registry, mail_uri);
1978 if (part_list == NULL) {
1979 EMailBackend *mail_backend;
1980 EMailSession *mail_session;
1981 EMailParser *parser;
1982
1983 mail_backend = e_mail_reader_get_backend (reader);
1984 mail_session = e_mail_backend_get_session (mail_backend);
1985
1986 parser = e_mail_parser_new (CAMEL_SESSION (mail_session));
1987
1988 part_list = e_mail_parser_parse_sync (
1989 parser,
1990 async_context->folder,
1991 async_context->message_uid,
1992 async_context->message,
1993 cancellable);
1994
1995 g_object_unref (parser);
1996
1997 if (part_list == NULL)
1998 camel_object_bag_abort (registry, mail_uri);
1999 else
2000 camel_object_bag_add (registry, mail_uri, part_list);
2001 }
2002
2003 g_free (mail_uri);
2004
2005 async_context->part_list = part_list;
2006 }
2007
2008 void
2009 e_mail_reader_parse_message (EMailReader *reader,
2010 CamelFolder *folder,
2011 const gchar *message_uid,
2012 CamelMimeMessage *message,
2013 GCancellable *cancellable,
2014 GAsyncReadyCallback callback,
2015 gpointer user_data)
2016 {
2017 GSimpleAsyncResult *simple;
2018 AsyncContext *async_context;
2019 EActivity *activity;
2020
2021 g_return_if_fail (E_IS_MAIL_READER (reader));
2022 g_return_if_fail (CAMEL_IS_FOLDER (folder));
2023 g_return_if_fail (message_uid != NULL);
2024 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2025
2026 activity = e_mail_reader_new_activity (reader);
2027 e_activity_set_cancellable (activity, cancellable);
2028 e_activity_set_text (activity, _("Parsing message"));
2029
2030 async_context = g_slice_new0 (AsyncContext);
2031 async_context->activity = activity; /* takes ownership */
2032 async_context->folder = g_object_ref (folder);
2033 async_context->message_uid = g_strdup (message_uid);
2034 async_context->message = g_object_ref (message);
2035
2036 simple = g_simple_async_result_new (
2037 G_OBJECT (reader), callback, user_data,
2038 e_mail_reader_parse_message);
2039
2040 g_simple_async_result_set_check_cancellable (simple, cancellable);
2041
2042 g_simple_async_result_set_op_res_gpointer (
2043 simple, async_context, (GDestroyNotify) async_context_free);
2044
2045 g_simple_async_result_run_in_thread (
2046 simple, mail_reader_parse_message_run,
2047 G_PRIORITY_DEFAULT, cancellable);
2048
2049 g_object_unref (simple);
2050 }
2051
2052 EMailPartList *
2053 e_mail_reader_parse_message_finish (EMailReader *reader,
2054 GAsyncResult *result)
2055 {
2056 GSimpleAsyncResult *simple;
2057 AsyncContext *async_context;
2058
2059 g_return_val_if_fail (
2060 g_simple_async_result_is_valid (
2061 result, G_OBJECT (reader),
2062 e_mail_reader_parse_message), NULL);
2063
2064 simple = G_SIMPLE_ASYNC_RESULT (result);
2065 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2066
2067 if (async_context->part_list != NULL)
2068 g_object_ref (async_context->part_list);
2069
2070 return async_context->part_list;
2071 }