No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-mail-backend.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-mail-backend.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * e-mail-backend.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 * Authors:
18 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 * Copyright (C) 2009 Intel Corporation
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include "e-mail-backend.h"
30
31 #include <string.h>
32 #include <glib/gstdio.h>
33 #include <glib/gi18n-lib.h>
34
35 #include <shell/e-shell.h>
36
37 #include <libevolution-utils/e-alert-dialog.h>
38 #include <libevolution-utils/e-alert-sink.h>
39
40 #include <libemail-engine/e-mail-folder-utils.h>
41 #include <libemail-engine/e-mail-session.h>
42 #include <libemail-engine/e-mail-store-utils.h>
43 #include <libemail-engine/mail-config.h>
44 #include <libemail-engine/mail-folder-cache.h>
45 #include <libemail-engine/mail-ops.h>
46
47 #include <mail/e-mail-migrate.h>
48 #include <mail/e-mail-ui-session.h>
49 #include <mail/em-event.h>
50 #include <mail/em-folder-tree-model.h>
51 #include <mail/em-utils.h>
52 #include <mail/mail-autofilter.h>
53 #include <mail/mail-send-recv.h>
54 #include <mail/mail-vfolder-ui.h>
55
56 #define E_MAIL_BACKEND_GET_PRIVATE(obj) \
57 (G_TYPE_INSTANCE_GET_PRIVATE \
58 ((obj), E_TYPE_MAIL_BACKEND, EMailBackendPrivate))
59
60 #define E_MAIL_BACKEND_GET_PRIVATE(obj) \
61 (G_TYPE_INSTANCE_GET_PRIVATE \
62 ((obj), E_TYPE_MAIL_BACKEND, EMailBackendPrivate))
63
64 #define QUIT_POLL_INTERVAL 1 /* seconds */
65
66 struct _EMailBackendPrivate {
67 EMailSession *session;
68 GHashTable *jobs;
69 };
70
71 enum {
72 PROP_0,
73 PROP_SESSION
74 };
75
76 /* FIXME Kill this thing. It's a horrible hack. */
77 extern gint camel_application_is_exiting;
78
79 G_DEFINE_ABSTRACT_TYPE (
80 EMailBackend,
81 e_mail_backend,
82 E_TYPE_SHELL_BACKEND)
83
84 static const gchar *
85 mail_shell_backend_get_data_dir (EShellBackend *backend)
86 {
87 return mail_session_get_data_dir ();
88 }
89
90 static const gchar *
91 mail_shell_backend_get_config_dir (EShellBackend *backend)
92 {
93 return mail_session_get_config_dir ();
94 }
95
96 static gchar *
97 mail_backend_uri_to_evname (const gchar *uri,
98 const gchar *prefix)
99 {
100 const gchar *data_dir;
101 gchar *basename;
102 gchar *filename;
103 gchar *safe;
104
105 /* Converts a folder URI to a GalView filename. */
106
107 data_dir = mail_session_get_data_dir ();
108
109 safe = g_strdup (uri);
110 e_filename_make_safe (safe);
111 basename = g_strdup_printf ("%s%s.xml", prefix, safe);
112 filename = g_build_filename (data_dir, basename, NULL);
113 g_free (basename);
114 g_free (safe);
115
116 return filename;
117 }
118
119 /* Callback for various asynchronous CamelStore operations where
120 * the EActivity's reference count is used as a counting semaphore. */
121 static void
122 mail_backend_store_operation_done_cb (CamelStore *store,
123 GAsyncResult *result,
124 EActivity *activity)
125 {
126 /* FIXME Not checking result for error. To fix this, we need
127 * separate callbacks to call different finish functions
128 * and then submit an EAlert on error. */
129
130 g_object_unref (activity);
131 }
132
133 static void
134 mail_backend_prepare_for_offline_cb (EShell *shell,
135 EActivity *activity,
136 EMailBackend *backend)
137 {
138 GtkWindow *window;
139 EMailSession *session;
140 EMailAccountStore *account_store;
141 GQueue queue = G_QUEUE_INIT;
142 gboolean synchronize = FALSE;
143
144 if (e_shell_backend_is_started (E_SHELL_BACKEND (backend))) {
145 if (!e_activity_get_cancellable (activity)) {
146 GCancellable *cancellable;
147
148 cancellable = camel_operation_new ();
149 e_activity_set_cancellable (activity, cancellable);
150 g_object_unref (cancellable);
151 }
152
153 e_shell_backend_add_activity (E_SHELL_BACKEND (backend), activity);
154 }
155
156 window = e_shell_get_active_window (shell);
157 session = e_mail_backend_get_session (backend);
158 account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (session));
159
160 if (e_shell_get_network_available (shell) &&
161 e_shell_backend_is_started (E_SHELL_BACKEND (backend)))
162 synchronize = em_utils_prompt_user (
163 window, NULL, "mail:ask-quick-offline", NULL);
164
165 if (!synchronize) {
166 mail_cancel_all ();
167 camel_session_set_network_available (
168 CAMEL_SESSION (session), FALSE);
169 }
170
171 e_mail_account_store_queue_enabled_services (account_store, &queue);
172 while (!g_queue_is_empty (&queue)) {
173 CamelService *service;
174
175 service = g_queue_pop_head (&queue);
176 if (service == NULL)
177 continue;
178
179 if (CAMEL_IS_STORE (service))
180 e_mail_store_go_offline (
181 CAMEL_STORE (service), G_PRIORITY_DEFAULT,
182 e_activity_get_cancellable (activity),
183 (GAsyncReadyCallback) mail_backend_store_operation_done_cb,
184 g_object_ref (activity));
185 }
186 }
187
188 static void
189 mail_backend_prepare_for_online_cb (EShell *shell,
190 EActivity *activity,
191 EMailBackend *backend)
192 {
193 EMailSession *session;
194 EMailAccountStore *account_store;
195 GQueue queue = G_QUEUE_INIT;
196
197 if (e_shell_backend_is_started (E_SHELL_BACKEND (backend))) {
198 if (!e_activity_get_cancellable (activity)) {
199 GCancellable *cancellable;
200
201 cancellable = camel_operation_new ();
202 e_activity_set_cancellable (activity, cancellable);
203 g_object_unref (cancellable);
204 }
205
206 e_shell_backend_add_activity (E_SHELL_BACKEND (backend), activity);
207 }
208
209 session = e_mail_backend_get_session (backend);
210 account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (session));
211
212 camel_session_set_online (CAMEL_SESSION (session), TRUE);
213
214 e_mail_account_store_queue_enabled_services (account_store, &queue);
215 while (!g_queue_is_empty (&queue)) {
216 CamelService *service;
217
218 service = g_queue_pop_head (&queue);
219 if (service == NULL)
220 continue;
221
222 if (CAMEL_IS_STORE (service))
223 e_mail_store_go_online (
224 CAMEL_STORE (service), G_PRIORITY_DEFAULT,
225 e_activity_get_cancellable (activity),
226 (GAsyncReadyCallback) mail_backend_store_operation_done_cb,
227 g_object_ref (activity));
228 }
229 }
230
231 /* Helper for mail_backend_prepare_for_quit_cb() */
232 static void
233 mail_backend_delete_junk (CamelService *service,
234 EMailBackend *backend)
235 {
236 CamelFolder *folder;
237 GPtrArray *uids;
238 guint32 flags;
239 guint32 mask;
240 guint ii;
241
242 /* FIXME camel_store_get_junk_folder_sync() may block. */
243 folder = camel_store_get_junk_folder_sync (
244 CAMEL_STORE (service), NULL, NULL);
245 if (folder == NULL)
246 return;
247
248 uids = camel_folder_get_uids (folder);
249 flags = mask = CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN;
250
251 camel_folder_freeze (folder);
252
253 for (ii = 0; ii < uids->len; ii++) {
254 const gchar *uid = uids->pdata[ii];
255 camel_folder_set_message_flags (folder, uid, flags, mask);
256 }
257
258 camel_folder_thaw (folder);
259
260 camel_folder_free_uids (folder, uids);
261 g_object_unref (folder);
262 }
263
264 /* Helper for mail_backend_prepare_for_quit_cb() */
265 static gboolean
266 mail_backend_poll_to_quit (EActivity *activity)
267 {
268 return mail_msg_active ();
269 }
270
271 /* Helper for mail_backend_prepare_for_quit_cb() */
272 static void
273 mail_backend_ready_to_quit (EActivity *activity)
274 {
275 emu_free_mail_cache (g_object_unref, g_object_ref (activity));
276
277 /* Do this last. It may terminate the process. */
278 g_object_unref (activity);
279 }
280
281 static gboolean
282 mail_backend_check_enabled (ESourceRegistry *registry,
283 ESource *source)
284 {
285 gboolean enabled;
286 gchar *parent_uid;
287
288 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
289 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
290
291 enabled = e_source_get_enabled (source);
292 parent_uid = e_source_dup_parent (source);
293
294 while (enabled && parent_uid != NULL) {
295 ESource *parent;
296
297 parent = e_source_registry_ref_source (registry, parent_uid);
298
299 g_free (parent_uid);
300 parent_uid = NULL;
301
302 if (parent != NULL) {
303 enabled = e_source_get_enabled (parent);
304 parent_uid = e_source_dup_parent (parent);
305 g_object_unref (parent);
306 }
307 }
308
309 g_free (parent_uid);
310
311 return enabled;
312 }
313
314 static gboolean
315 mail_backend_service_is_enabled (ESourceRegistry *registry,
316 CamelService *service)
317 {
318 const gchar *uid;
319 ESource *source;
320 gboolean enabled;
321
322 g_return_val_if_fail (registry != NULL, FALSE);
323 g_return_val_if_fail (service != NULL, FALSE);
324
325 uid = camel_service_get_uid (service);
326 g_return_val_if_fail (uid != NULL, FALSE);
327
328 source = e_source_registry_ref_source (registry, uid);
329 if (!source)
330 return FALSE;
331
332 enabled = mail_backend_check_enabled (registry, source);
333 g_object_unref (source);
334
335 return enabled;
336 }
337
338 static void
339 mail_backend_prepare_for_quit_cb (EShell *shell,
340 EActivity *activity,
341 EMailBackend *backend)
342 {
343 EMailSession *session;
344 ESourceRegistry *registry;
345 GList *list, *link;
346 gboolean delete_junk;
347 gboolean empty_trash;
348
349 session = e_mail_backend_get_session (backend);
350 registry = e_shell_get_registry (shell);
351
352 delete_junk = e_mail_backend_delete_junk_policy_decision (backend);
353 empty_trash = e_mail_backend_empty_trash_policy_decision (backend);
354
355 camel_application_is_exiting = TRUE;
356
357 mail_vfolder_shutdown ();
358
359 /* Cancel all pending activities. */
360 mail_cancel_all ();
361
362 list = camel_session_list_services (CAMEL_SESSION (session));
363
364 if (delete_junk) {
365 for (link = list; link != NULL; link = g_list_next (link)) {
366 CamelService *service;
367
368 service = CAMEL_SERVICE (link->data);
369
370 if (!CAMEL_IS_STORE (service) ||
371 !mail_backend_service_is_enabled (registry, service))
372 continue;
373
374 mail_backend_delete_junk (service, backend);
375 }
376 }
377
378 for (link = list; link != NULL; link = g_list_next (link)) {
379 CamelService *service;
380
381 service = CAMEL_SERVICE (link->data);
382
383 if (!CAMEL_IS_STORE (service) ||
384 !mail_backend_service_is_enabled (registry, service))
385 continue;
386
387 /* FIXME Not passing a GCancellable. */
388 /* FIXME This operation should be queued. */
389 camel_store_synchronize (
390 CAMEL_STORE (service),
391 empty_trash, G_PRIORITY_DEFAULT,
392 NULL, (GAsyncReadyCallback)
393 mail_backend_store_operation_done_cb,
394 g_object_ref (activity));
395 }
396
397 g_list_free_full (list, (GDestroyNotify) g_object_unref);
398
399 /* Now we poll until all activities are actually cancelled or finished.
400 * Reffing the activity delays quitting; the reference count
401 * acts like a counting semaphore. */
402 if (mail_msg_active ())
403 g_timeout_add_seconds_full (
404 G_PRIORITY_DEFAULT, QUIT_POLL_INTERVAL,
405 (GSourceFunc) mail_backend_poll_to_quit,
406 g_object_ref (activity),
407 (GDestroyNotify) mail_backend_ready_to_quit);
408 else
409 mail_backend_ready_to_quit (g_object_ref (activity));
410 }
411
412 static void
413 mail_backend_quit_requested_cb (EShell *shell,
414 EShellQuitReason reason,
415 EShellBackend *mail_shell_backend)
416 {
417 EMailBackend *backend;
418 EMailSession *session;
419 CamelFolder *folder;
420 GtkWindow *window;
421 gint response;
422
423 window = e_shell_get_active_window (shell);
424
425 /* We can quit immediately if offline. */
426 if (!e_shell_get_online (shell))
427 return;
428
429 /* Or if another Evolution process asked us to. */
430 if (reason == E_SHELL_QUIT_REMOTE_REQUEST)
431 return;
432
433 /* In express mode, don't raise mail request in non mail window. */
434 if (e_shell_get_express_mode (shell) &&
435 strcmp (e_shell_window_get_active_view ((EShellWindow *) window), "mail") != 0)
436 return;
437
438 if (!e_shell_backend_is_started (mail_shell_backend))
439 return;
440
441 /* Check Outbox for any unsent messages. */
442
443 backend = E_MAIL_BACKEND (mail_shell_backend);
444 session = e_mail_backend_get_session (backend);
445
446 folder = e_mail_session_get_local_folder (
447 session, E_MAIL_LOCAL_FOLDER_OUTBOX);
448 if (folder == NULL)
449 return;
450
451 if (camel_folder_summary_get_visible_count (folder->summary) == 0)
452 return;
453
454 response = e_alert_run_dialog_for_args (
455 window, "mail:exit-unsaved", NULL);
456
457 if (response == GTK_RESPONSE_YES)
458 return;
459
460 e_shell_cancel_quit (shell);
461 }
462
463 static void
464 mail_backend_folder_deleted_cb (MailFolderCache *folder_cache,
465 CamelStore *store,
466 const gchar *folder_name,
467 EMailBackend *backend)
468 {
469 EShell *shell;
470 CamelStoreClass *class;
471 ESourceRegistry *registry;
472 EShellBackend *shell_backend;
473 EMailSession *session;
474 EAlertSink *alert_sink;
475 GList *list, *link;
476 const gchar *extension_name;
477 const gchar *local_drafts_folder_uri;
478 const gchar *local_sent_folder_uri;
479 gchar *uri;
480
481 /* Check whether the deleted folder was a designated Drafts or
482 * Sent folder for any mail account, and if so revert the setting
483 * to the equivalent local folder, which is always present. */
484
485 shell_backend = E_SHELL_BACKEND (backend);
486 shell = e_shell_backend_get_shell (shell_backend);
487 registry = e_shell_get_registry (shell);
488
489 class = CAMEL_STORE_GET_CLASS (store);
490 g_return_if_fail (class->equal_folder_name != NULL);
491
492 session = e_mail_backend_get_session (backend);
493 alert_sink = e_mail_backend_get_alert_sink (backend);
494
495 local_drafts_folder_uri =
496 e_mail_session_get_local_folder_uri (
497 session, E_MAIL_LOCAL_FOLDER_DRAFTS);
498
499 local_sent_folder_uri =
500 e_mail_session_get_local_folder_uri (
501 session, E_MAIL_LOCAL_FOLDER_SENT);
502
503 uri = e_mail_folder_uri_build (store, folder_name);
504
505 extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
506 list = e_source_registry_list_sources (registry, extension_name);
507
508 for (link = list; link != NULL; link = g_list_next (link)) {
509 ESource *source = E_SOURCE (link->data);
510 ESourceExtension *extension;
511 const gchar *drafts_folder_uri;
512
513 extension = e_source_get_extension (source, extension_name);
514
515 drafts_folder_uri =
516 e_source_mail_composition_get_drafts_folder (
517 E_SOURCE_MAIL_COMPOSITION (extension));
518
519 if (!drafts_folder_uri)
520 continue;
521
522 if (class->equal_folder_name (drafts_folder_uri, uri)) {
523 GError *error = NULL;
524
525 e_source_mail_composition_set_drafts_folder (
526 E_SOURCE_MAIL_COMPOSITION (extension),
527 local_drafts_folder_uri);
528
529 /* FIXME This is a blocking D-Bus method call. */
530 if (!e_source_write_sync (source, NULL, &error)) {
531 g_warning ("%s", error->message);
532 g_error_free (error);
533 }
534 }
535 }
536
537 g_list_free_full (list, (GDestroyNotify) g_object_unref);
538
539 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
540 list = e_source_registry_list_sources (registry, extension_name);
541
542 for (link = list; link != NULL; link = g_list_next (link)) {
543 ESource *source = E_SOURCE (link->data);
544 ESourceExtension *extension;
545 const gchar *sent_folder_uri;
546
547 extension = e_source_get_extension (source, extension_name);
548
549 sent_folder_uri =
550 e_source_mail_submission_get_sent_folder (
551 E_SOURCE_MAIL_SUBMISSION (extension));
552
553 if (sent_folder_uri == NULL)
554 continue;
555
556 if (class->equal_folder_name (sent_folder_uri, uri)) {
557 GError *error = NULL;
558
559 e_source_mail_submission_set_sent_folder (
560 E_SOURCE_MAIL_SUBMISSION (extension),
561 local_sent_folder_uri);
562
563 /* FIXME This is a blocking D-Bus method call. */
564 if (!e_source_write_sync (source, NULL, &error)) {
565 g_warning ("%s", error->message);
566 g_error_free (error);
567 }
568 }
569 }
570
571 g_list_free_full (list, (GDestroyNotify) g_object_unref);
572
573 g_free (uri);
574
575 /* This does something completely different.
576 * XXX Make it a separate signal handler? */
577 mail_filter_delete_folder (store, folder_name, alert_sink);
578 }
579
580 static void
581 mail_backend_folder_renamed_cb (MailFolderCache *folder_cache,
582 CamelStore *store,
583 const gchar *old_folder_name,
584 const gchar *new_folder_name,
585 EMailBackend *backend)
586 {
587 EShell *shell;
588 CamelStoreClass *class;
589 ESourceRegistry *registry;
590 EShellBackend *shell_backend;
591 GList *list, *link;
592 const gchar *extension_name;
593 gchar *old_uri;
594 gchar *new_uri;
595 gint ii;
596
597 const gchar *cachenames[] = {
598 "views/current_view-",
599 "views/custom_view-"
600 };
601
602 /* Check whether the renamed folder was a designated Drafts or
603 * Sent folder for any mail account, and if so update the setting
604 * to the new folder name. */
605
606 shell_backend = E_SHELL_BACKEND (backend);
607 shell = e_shell_backend_get_shell (shell_backend);
608 registry = e_shell_get_registry (shell);
609
610 class = CAMEL_STORE_GET_CLASS (store);
611 g_return_if_fail (class->equal_folder_name != NULL);
612
613 old_uri = e_mail_folder_uri_build (store, old_folder_name);
614 new_uri = e_mail_folder_uri_build (store, new_folder_name);
615
616 extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
617 list = e_source_registry_list_sources (registry, extension_name);
618
619 for (link = list; link != NULL; link = g_list_next (link)) {
620 ESource *source = E_SOURCE (link->data);
621 ESourceExtension *extension;
622 const gchar *drafts_folder_uri;
623 gboolean need_update;
624
625 extension = e_source_get_extension (source, extension_name);
626
627 drafts_folder_uri =
628 e_source_mail_composition_get_drafts_folder (
629 E_SOURCE_MAIL_COMPOSITION (extension));
630
631 need_update =
632 (drafts_folder_uri != NULL) &&
633 class->equal_folder_name (drafts_folder_uri, old_uri);
634
635 if (need_update) {
636 GError *error = NULL;
637
638 e_source_mail_composition_set_drafts_folder (
639 E_SOURCE_MAIL_COMPOSITION (extension),
640 new_uri);
641
642 /* FIXME This is a blocking D-Bus method call. */
643 if (!e_source_write_sync (source, NULL, &error)) {
644 g_warning ("%s", error->message);
645 g_error_free (error);
646 }
647 }
648 }
649
650 g_list_free_full (list, (GDestroyNotify) g_object_unref);
651
652 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
653 list = e_source_registry_list_sources (registry, extension_name);
654
655 for (link = list; link != NULL; link = g_list_next (link)) {
656 ESource *source = E_SOURCE (link->data);
657 ESourceExtension *extension;
658 const gchar *sent_folder_uri;
659 gboolean need_update;
660
661 extension = e_source_get_extension (source, extension_name);
662
663 sent_folder_uri =
664 e_source_mail_submission_get_sent_folder (
665 E_SOURCE_MAIL_SUBMISSION (extension));
666
667 need_update =
668 (sent_folder_uri != NULL) &&
669 class->equal_folder_name (sent_folder_uri, old_uri);
670
671 if (need_update) {
672 GError *error = NULL;
673
674 e_source_mail_submission_set_sent_folder (
675 E_SOURCE_MAIL_SUBMISSION (extension),
676 new_uri);
677
678 /* FIXME This is a blocking D-Bus method call. */
679 if (!e_source_write_sync (source, NULL, &error)) {
680 g_warning ("%s", error->message);
681 g_error_free (error);
682 }
683 }
684 }
685
686 g_list_free_full (list, (GDestroyNotify) g_object_unref);
687
688 /* Rename GalView files. */
689
690 for (ii = 0; ii < G_N_ELEMENTS (cachenames); ii++) {
691 gchar *oldname;
692 gchar *newname;
693
694 oldname = mail_backend_uri_to_evname (old_uri, cachenames[ii]);
695 newname = mail_backend_uri_to_evname (new_uri, cachenames[ii]);
696
697 /* Ignore errors; doesn't matter. */
698 g_rename (oldname, newname);
699
700 g_free (oldname);
701 g_free (newname);
702 }
703
704 g_free (old_uri);
705 g_free (new_uri);
706
707 /* This does something completely different.
708 * XXX Make it a separate signal handler? */
709 mail_filter_rename_folder (
710 store, old_folder_name, new_folder_name);
711 }
712
713 static void
714 mail_backend_folder_changed_cb (MailFolderCache *folder_cache,
715 CamelStore *store,
716 const gchar *folder_name,
717 gint new_messages,
718 const gchar *msg_uid,
719 const gchar *msg_sender,
720 const gchar *msg_subject,
721 EMailBackend *mail_backend)
722 {
723 EMEvent *event = em_event_peek ();
724 EMEventTargetFolder *target;
725 EMFolderTreeModel *model;
726 gchar *folder_uri;
727 gint folder_type;
728 CamelFolderInfoFlags flags = 0;
729
730 folder_uri = e_mail_folder_uri_build (store, folder_name);
731
732 if (folder_uri != NULL) {
733 CamelFolder *folder = NULL;
734
735 if (mail_folder_cache_get_folder_from_uri (
736 folder_cache, folder_uri, &folder)) {
737 if (folder != NULL &&
738 !mail_folder_cache_get_folder_info_flags (
739 folder_cache, folder, &flags)) {
740 g_free (folder_uri);
741 g_return_if_reached ();
742 }
743 }
744
745 if (folder != NULL)
746 g_object_unref (folder);
747 }
748
749 target = em_event_target_new_folder (
750 event, store, folder_uri, new_messages,
751 msg_uid, msg_sender, msg_subject);
752
753 if (folder_uri)
754 g_free (folder_uri);
755
756 folder_type = (flags & CAMEL_FOLDER_TYPE_MASK);
757 target->is_inbox = (folder_type == CAMEL_FOLDER_TYPE_INBOX);
758
759 model = em_folder_tree_model_get_default ();
760 target->display_name = em_folder_tree_model_get_folder_name (
761 model, store, folder_name);
762
763 if (target->new > 0) {
764 EShell *shell;
765 EShellBackend *shell_backend;
766
767 shell_backend = E_SHELL_BACKEND (mail_backend);
768 shell = e_shell_backend_get_shell (shell_backend);
769 e_shell_event (shell, "mail-icon", (gpointer) "mail-unread");
770 }
771
772 /**
773 * @Event: folder.changed
774 * @Title: Folder changed
775 * @Target: EMEventTargetFolder
776 *
777 * folder.changed is emitted whenever a folder changes. There is no
778 * detail on how the folder has changed.
779 *
780 * UPDATE: We tell the number of new UIDs added rather than the new
781 * mails received.
782 */
783 e_event_emit (
784 (EEvent *) event, "folder.changed",
785 (EEventTarget *) target);
786 }
787
788 static void
789 mail_backend_job_started_cb (CamelSession *session,
790 GCancellable *cancellable,
791 EShellBackend *shell_backend)
792 {
793 EMailBackendPrivate *priv;
794 EActivity *activity;
795
796 priv = E_MAIL_BACKEND_GET_PRIVATE (shell_backend);
797
798 /* Make sure this operation shows up in the user interface.
799 * This message should get overridden, if not it's a bug in
800 * whatever CamelService submitted this. */
801 camel_operation_push_message (
802 cancellable, _("Unknown background operation"));
803
804 activity = e_activity_new ();
805 e_activity_set_cancellable (activity, cancellable);
806 e_shell_backend_add_activity (shell_backend, activity);
807
808 /* The hash table takes ownership of the activity. */
809 g_hash_table_insert (priv->jobs, cancellable, activity);
810 }
811
812 static void
813 mail_backend_job_finished_cb (CamelSession *session,
814 GCancellable *cancellable,
815 const GError *error,
816 EShellBackend *shell_backend)
817 {
818 EMailBackendPrivate *priv;
819 EShellBackendClass *class;
820 EActivity *activity;
821 const gchar *description;
822
823 priv = E_MAIL_BACKEND_GET_PRIVATE (shell_backend);
824 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
825
826 /* Pop the generic "background operation" message. */
827 camel_operation_pop_message (cancellable);
828
829 activity = g_hash_table_lookup (priv->jobs, cancellable);
830 description = e_activity_get_text (activity);
831
832 if (e_activity_handle_cancellation (activity, error)) {
833 /* nothing to do */
834
835 } else if (error != NULL) {
836 EShell *shell;
837 GtkApplication *application;
838 GList *list, *iter;
839
840 shell = e_shell_backend_get_shell (shell_backend);
841
842 application = GTK_APPLICATION (shell);
843 list = gtk_application_get_windows (application);
844
845 /* Submit the error to an appropriate EAlertSink. */
846 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
847 EShellView *shell_view;
848 EShellContent *shell_content;
849
850 if (!E_IS_SHELL_WINDOW (iter->data))
851 continue;
852
853 shell_view = e_shell_window_peek_shell_view (
854 E_SHELL_WINDOW (iter->data), class->name);
855
856 if (!E_IS_SHELL_VIEW (shell_view))
857 continue;
858
859 shell_content =
860 e_shell_view_get_shell_content (shell_view);
861
862 if (description != NULL && *description != '\0')
863 e_alert_submit (
864 E_ALERT_SINK (shell_content),
865 "mail:async-error", description,
866 error->message, NULL);
867 else
868 e_alert_submit (
869 E_ALERT_SINK (shell_content),
870 "mail:async-error-nodescribe",
871 error->message, NULL);
872
873 break;
874 }
875 }
876
877 g_hash_table_remove (priv->jobs, cancellable);
878 }
879
880 static void
881 mail_backend_get_property (GObject *object,
882 guint property_id,
883 GValue *value,
884 GParamSpec *pspec)
885 {
886 switch (property_id) {
887 case PROP_SESSION:
888 g_value_set_object (
889 value,
890 e_mail_backend_get_session (
891 E_MAIL_BACKEND (object)));
892 return;
893 }
894
895 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
896 }
897
898 static void
899 mail_backend_dispose (GObject *object)
900 {
901 EMailBackendPrivate *priv;
902
903 priv = E_MAIL_BACKEND_GET_PRIVATE (object);
904
905 if (priv->session != NULL) {
906 g_signal_handlers_disconnect_matched (
907 priv->session, G_SIGNAL_MATCH_DATA,
908 0, 0, NULL, NULL, object);
909 camel_session_remove_services (
910 CAMEL_SESSION (priv->session));
911 g_object_unref (priv->session);
912 priv->session = NULL;
913 }
914
915 /* There should be no unfinished jobs left. */
916 g_warn_if_fail (g_hash_table_size (priv->jobs) == 0);
917
918 /* Chain up to parent's dispose() method. */
919 G_OBJECT_CLASS (e_mail_backend_parent_class)->dispose (object);
920 }
921
922 static void
923 mail_backend_finalize (GObject *object)
924 {
925 EMailBackendPrivate *priv;
926
927 priv = E_MAIL_BACKEND_GET_PRIVATE (object);
928
929 g_hash_table_destroy (priv->jobs);
930
931 /* Chain up to parent's finalize() method. */
932 G_OBJECT_CLASS (e_mail_backend_parent_class)->finalize (object);
933
934 camel_shutdown ();
935 }
936
937 static void
938 mail_backend_add_store (EMailSession *session,
939 CamelStore *store,
940 EMailBackend *backend)
941 {
942 EMFolderTreeModel *model;
943
944 model = em_folder_tree_model_get_default ();
945 em_folder_tree_model_add_store (model, store);
946 }
947
948 static void
949 mail_backend_remove_store (EMailSession *session,
950 CamelStore *store,
951 EMailBackend *backend)
952 {
953 EMFolderTreeModel *model;
954
955 model = em_folder_tree_model_get_default ();
956 em_folder_tree_model_remove_store (model, store);
957 }
958
959 #define SET_ACTIVITY(cancellable, activity) \
960 g_object_set_data (G_OBJECT (cancellable), "e-activity", activity)
961 #define GET_ACTIVITY(cancellable) \
962 g_object_get_data (G_OBJECT (cancellable), "e-activity")
963
964 static void
965 mail_mt_create_activity (GCancellable *cancellable)
966 {
967 EActivity *activity;
968
969 activity = e_activity_new ();
970 e_activity_set_percent (activity, 0.0);
971 e_activity_set_cancellable (activity, cancellable);
972 SET_ACTIVITY (cancellable, activity);
973 }
974
975 static void
976 mail_mt_submit_activity (GCancellable *cancellable)
977 {
978 EShell *shell;
979 EShellBackend *shell_backend;
980 EActivity *activity;
981
982 shell = e_shell_get_default ();
983 shell_backend = e_shell_get_backend_by_name (
984 shell, "mail");
985
986 activity = GET_ACTIVITY (cancellable);
987 if (activity)
988 e_shell_backend_add_activity (shell_backend, activity);
989
990 }
991
992 static void
993 mail_mt_free_activity (GCancellable *cancellable)
994 {
995 EActivity *activity = GET_ACTIVITY (cancellable);
996
997 if (activity)
998 g_object_unref (activity);
999 }
1000
1001 static void
1002 mail_mt_complete_activity (GCancellable *cancellable)
1003 {
1004 EActivity *activity = GET_ACTIVITY (cancellable);
1005
1006 if (activity)
1007 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
1008 }
1009
1010 static void
1011 mail_mt_cancel_activity (GCancellable *cancellable)
1012 {
1013 EActivity *activity = GET_ACTIVITY (cancellable);
1014
1015 if (activity)
1016 e_activity_set_state (activity, E_ACTIVITY_CANCELLED);
1017 }
1018
1019 static void
1020 mail_mt_alert_error (GCancellable *cancellable,
1021 const gchar *what,
1022 const gchar *message)
1023 {
1024 EShell *shell;
1025 EShellView *shell_view;
1026 EShellWindow *shell_window = NULL;
1027 EShellContent *shell_content;
1028 GList *list, *iter;
1029 GtkApplication *application;
1030
1031 shell = e_shell_get_default ();
1032 application = GTK_APPLICATION (shell);
1033 list = gtk_application_get_windows (application);
1034
1035 /* Find the most recently used EShellWindow. */
1036 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
1037 if (E_IS_SHELL_WINDOW (iter->data)) {
1038 shell_window = E_SHELL_WINDOW (iter->data);
1039 break;
1040 }
1041 }
1042
1043 /* If we can't find an EShellWindow then... well, screw it. */
1044 if (shell_window == NULL)
1045 return;
1046
1047 shell_view = e_shell_window_get_shell_view (
1048 shell_window, "mail");
1049 shell_content = e_shell_view_get_shell_content (shell_view);
1050
1051 if (what) {
1052 e_alert_submit (
1053 E_ALERT_SINK (shell_content),
1054 "mail:async-error", what,
1055 message, NULL);
1056 } else
1057 e_alert_submit (
1058 E_ALERT_SINK (shell_content),
1059 "mail:async-error-nodescribe",
1060 message, NULL);
1061 }
1062
1063 static EAlertSink *
1064 mail_mt_get_alert_sink ()
1065 {
1066 EShell *shell;
1067 EShellBackend *shell_backend;
1068
1069 shell = e_shell_get_default ();
1070 shell_backend = e_shell_get_backend_by_name (
1071 shell, "mail");
1072
1073 return e_mail_backend_get_alert_sink (E_MAIL_BACKEND (shell_backend));
1074 }
1075
1076 static void
1077 mail_backend_constructed (GObject *object)
1078 {
1079 EMailBackendPrivate *priv;
1080 EShell *shell;
1081 EShellBackend *shell_backend;
1082 MailFolderCache *folder_cache;
1083 ESourceRegistry *registry;
1084
1085 priv = E_MAIL_BACKEND_GET_PRIVATE (object);
1086
1087 shell_backend = E_SHELL_BACKEND (object);
1088 shell = e_shell_backend_get_shell (shell_backend);
1089
1090 if (camel_init (e_get_user_data_dir (), TRUE) != 0)
1091 exit (0);
1092
1093 registry = e_shell_get_registry (shell);
1094 priv->session = e_mail_ui_session_new (registry);
1095
1096 g_signal_connect (
1097 priv->session, "flush-outbox",
1098 G_CALLBACK (mail_send), priv->session);
1099
1100 /* Propagate "activity-added" signals from
1101 * the mail session to the shell backend. */
1102 g_signal_connect_swapped (
1103 priv->session, "activity-added",
1104 G_CALLBACK (e_shell_backend_add_activity),
1105 shell_backend);
1106
1107 g_signal_connect (
1108 priv->session, "job-started",
1109 G_CALLBACK (mail_backend_job_started_cb),
1110 shell_backend);
1111
1112 g_signal_connect (
1113 priv->session, "job-finished",
1114 G_CALLBACK (mail_backend_job_finished_cb),
1115 shell_backend);
1116
1117 g_signal_connect (
1118 priv->session, "store-added",
1119 G_CALLBACK (mail_backend_add_store),
1120 shell_backend);
1121
1122 g_signal_connect (
1123 priv->session, "store-removed",
1124 G_CALLBACK (mail_backend_remove_store),
1125 shell_backend);
1126
1127 g_signal_connect (
1128 shell, "prepare-for-offline",
1129 G_CALLBACK (mail_backend_prepare_for_offline_cb),
1130 shell_backend);
1131
1132 g_signal_connect (
1133 shell, "prepare-for-online",
1134 G_CALLBACK (mail_backend_prepare_for_online_cb),
1135 shell_backend);
1136
1137 g_signal_connect (
1138 shell, "prepare-for-quit",
1139 G_CALLBACK (mail_backend_prepare_for_quit_cb),
1140 shell_backend);
1141
1142 g_signal_connect (
1143 shell, "quit-requested",
1144 G_CALLBACK (mail_backend_quit_requested_cb),
1145 shell_backend);
1146
1147 folder_cache = e_mail_session_get_folder_cache (priv->session);
1148
1149 g_signal_connect (
1150 folder_cache, "folder-deleted",
1151 G_CALLBACK (mail_backend_folder_deleted_cb),
1152 shell_backend);
1153
1154 g_signal_connect (
1155 folder_cache, "folder-renamed",
1156 G_CALLBACK (mail_backend_folder_renamed_cb),
1157 shell_backend);
1158
1159 g_signal_connect (
1160 folder_cache, "folder-changed",
1161 G_CALLBACK (mail_backend_folder_changed_cb), shell_backend);
1162
1163 mail_config_init (priv->session);
1164
1165 mail_msg_register_activities (
1166 mail_mt_create_activity,
1167 mail_mt_submit_activity,
1168 mail_mt_free_activity,
1169 mail_mt_complete_activity,
1170 mail_mt_cancel_activity,
1171 mail_mt_alert_error,
1172 mail_mt_get_alert_sink);
1173
1174 /* Chain up to parent's constructed() method. */
1175 G_OBJECT_CLASS (e_mail_backend_parent_class)->constructed (object);
1176 }
1177
1178 static void
1179 e_mail_backend_class_init (EMailBackendClass *class)
1180 {
1181 GObjectClass *object_class;
1182 EShellBackendClass *shell_backend_class;
1183
1184 g_type_class_add_private (class, sizeof (EMailBackendPrivate));
1185
1186 object_class = G_OBJECT_CLASS (class);
1187 object_class->get_property = mail_backend_get_property;
1188 object_class->dispose = mail_backend_dispose;
1189 object_class->finalize = mail_backend_finalize;
1190 object_class->constructed = mail_backend_constructed;
1191
1192 shell_backend_class = E_SHELL_BACKEND_CLASS (class);
1193 shell_backend_class->migrate = e_mail_migrate;
1194 shell_backend_class->get_data_dir = mail_shell_backend_get_data_dir;
1195 shell_backend_class->get_config_dir = mail_shell_backend_get_config_dir;
1196
1197 g_object_class_install_property (
1198 object_class,
1199 PROP_SESSION,
1200 g_param_spec_object (
1201 "session",
1202 NULL,
1203 NULL,
1204 E_TYPE_MAIL_SESSION,
1205 G_PARAM_READABLE));
1206 }
1207
1208 static void
1209 e_mail_backend_init (EMailBackend *backend)
1210 {
1211 backend->priv = E_MAIL_BACKEND_GET_PRIVATE (backend);
1212
1213 backend->priv->jobs = g_hash_table_new_full (
1214 (GHashFunc) g_direct_hash,
1215 (GEqualFunc) g_direct_equal,
1216 (GDestroyNotify) NULL,
1217 (GDestroyNotify) g_object_unref);
1218 }
1219
1220 EMailSession *
1221 e_mail_backend_get_session (EMailBackend *backend)
1222 {
1223 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
1224
1225 return backend->priv->session;
1226 }
1227
1228 EAlertSink *
1229 e_mail_backend_get_alert_sink (EMailBackend *backend)
1230 {
1231 EShell *shell;
1232 EShellView *shell_view;
1233 EShellBackend *shell_backend;
1234 EShellContent *shell_content;
1235 EShellWindow *shell_window = NULL;
1236 EShellBackendClass *class;
1237 GtkApplication *application;
1238 GList *list, *link;
1239
1240 /* XXX This is meant to be a convenient but temporary hack.
1241 * It digs through the list of available EShellWindows
1242 * to find a suitable EAlertSink. */
1243
1244 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
1245
1246 shell_backend = E_SHELL_BACKEND (backend);
1247 shell = e_shell_backend_get_shell (shell_backend);
1248
1249 application = GTK_APPLICATION (shell);
1250 list = gtk_application_get_windows (application);
1251
1252 /* Find the most recently used EShellWindow. */
1253 for (link = list; link != NULL; link = g_list_next (link)) {
1254 if (E_IS_SHELL_WINDOW (link->data)) {
1255 shell_window = E_SHELL_WINDOW (link->data);
1256 break;
1257 }
1258 }
1259
1260 g_return_val_if_fail (shell_window != NULL, NULL);
1261
1262 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
1263 shell_view = e_shell_window_get_shell_view (shell_window, class->name);
1264 shell_content = e_shell_view_get_shell_content (shell_view);
1265
1266 return E_ALERT_SINK (shell_content);
1267 }
1268
1269 gboolean
1270 e_mail_backend_delete_junk_policy_decision (EMailBackend *backend)
1271 {
1272 EMailBackendClass *class;
1273
1274 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), FALSE);
1275
1276 class = E_MAIL_BACKEND_GET_CLASS (backend);
1277 if (class->delete_junk_policy_decision == NULL)
1278 return FALSE;
1279
1280 return class->delete_junk_policy_decision (backend);
1281 }
1282
1283 gboolean
1284 e_mail_backend_empty_trash_policy_decision (EMailBackend *backend)
1285 {
1286 EMailBackendClass *class;
1287
1288 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), FALSE);
1289
1290 class = E_MAIL_BACKEND_GET_CLASS (backend);
1291 if (class->empty_trash_policy_decision == NULL)
1292 return FALSE;
1293
1294 return class->empty_trash_policy_decision (backend);
1295 }