1 /*
2 * e-mail-backend.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 * Copyright (C) 2009 Intel Corporation
21 *
22 */
23
24 #include "evolution-config.h"
25
26 #include "e-mail-backend.h"
27
28 #include <errno.h>
29 #include <string.h>
30 #include <glib/gstdio.h>
31 #include <glib/gi18n-lib.h>
32
33 #include <shell/e-shell.h>
34
35 #include <mail/e-mail-migrate.h>
36 #include <mail/e-mail-ui-session.h>
37 #include <mail/em-event.h>
38 #include <mail/em-folder-tree-model.h>
39 #include <mail/em-utils.h>
40 #include <mail/mail-autofilter.h>
41 #include <mail/mail-send-recv.h>
42 #include <mail/mail-vfolder-ui.h>
43
44 #define E_MAIL_BACKEND_GET_PRIVATE(obj) \
45 (G_TYPE_INSTANCE_GET_PRIVATE \
46 ((obj), E_TYPE_MAIL_BACKEND, EMailBackendPrivate))
47
48 #define E_MAIL_BACKEND_GET_PRIVATE(obj) \
49 (G_TYPE_INSTANCE_GET_PRIVATE \
50 ((obj), E_TYPE_MAIL_BACKEND, EMailBackendPrivate))
51
52 #define QUIT_POLL_INTERVAL 1 /* seconds */
53
54 struct _EMailBackendPrivate {
55 EMailSession *session;
56 GHashTable *jobs;
57 EMailSendAccountOverride *send_account_override;
58 EMailRemoteContent *remote_content;
59 EMailProperties *mail_properties;
60 };
61
62 enum {
63 PROP_0,
64 PROP_SESSION,
65 PROP_SEND_ACCOUNT_OVERRIDE,
66 PROP_REMOTE_CONTENT,
67 PROP_MAIL_PROPERTIES
68 };
69
G_DEFINE_ABSTRACT_TYPE(EMailBackend,e_mail_backend,E_TYPE_SHELL_BACKEND)70 G_DEFINE_ABSTRACT_TYPE (
71 EMailBackend,
72 e_mail_backend,
73 E_TYPE_SHELL_BACKEND)
74
75 static const gchar *
76 mail_shell_backend_get_data_dir (EShellBackend *backend)
77 {
78 return mail_session_get_data_dir ();
79 }
80
81 static const gchar *
mail_shell_backend_get_config_dir(EShellBackend * backend)82 mail_shell_backend_get_config_dir (EShellBackend *backend)
83 {
84 return mail_session_get_config_dir ();
85 }
86
87 static gchar *
mail_backend_uri_to_evname(const gchar * uri,const gchar * prefix)88 mail_backend_uri_to_evname (const gchar *uri,
89 const gchar *prefix)
90 {
91 const gchar *data_dir;
92 gchar *basename;
93 gchar *filename;
94 gchar *safe;
95
96 /* Converts a folder URI to a GalView filename. */
97
98 data_dir = mail_session_get_data_dir ();
99
100 safe = g_strdup (uri);
101 e_util_make_safe_filename (safe);
102 basename = g_strdup_printf ("%s%s.xml", prefix, safe);
103 filename = g_build_filename (data_dir, basename, NULL);
104 g_free (basename);
105 g_free (safe);
106
107 return filename;
108 }
109
110 static gboolean
mail_backend_any_store_requires_downsync(EMailAccountStore * account_store)111 mail_backend_any_store_requires_downsync (EMailAccountStore *account_store)
112 {
113 GQueue queue = G_QUEUE_INIT;
114
115 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (account_store), FALSE);
116
117 e_mail_account_store_queue_enabled_services (account_store, &queue);
118
119 while (!g_queue_is_empty (&queue)) {
120 CamelService *service;
121 CamelOfflineStore *offline_store;
122
123 service = g_queue_pop_head (&queue);
124
125 if (!CAMEL_IS_OFFLINE_STORE (service))
126 continue;
127
128 offline_store = CAMEL_OFFLINE_STORE (service);
129
130 if (camel_offline_store_requires_downsync (offline_store))
131 return TRUE;
132 }
133
134 return FALSE;
135 }
136
137 /* Callback for various asynchronous CamelStore operations where
138 * the EActivity's reference count is used as a counting semaphore. */
139 static void
mail_backend_store_operation_done_cb(CamelStore * store,GAsyncResult * result,EActivity * activity)140 mail_backend_store_operation_done_cb (CamelStore *store,
141 GAsyncResult *result,
142 EActivity *activity)
143 {
144 /* FIXME Not checking result for error. To fix this, we need
145 * separate callbacks to call different finish functions
146 * and then submit an EAlert on error. */
147
148 g_object_unref (activity);
149 }
150
151 static void
mail_backend_store_go_online_done_cb(CamelStore * store,GAsyncResult * result,EActivity * activity)152 mail_backend_store_go_online_done_cb (CamelStore *store,
153 GAsyncResult *result,
154 EActivity *activity)
155 {
156 CamelService *service;
157
158 service = CAMEL_SERVICE (store);
159
160 if (e_mail_store_go_online_finish (store, result, NULL) &&
161 camel_service_get_connection_status (service) == CAMEL_SERVICE_CONNECTED) {
162 CamelSession *session;
163
164 session = camel_service_ref_session (service);
165
166 if (E_IS_MAIL_SESSION (session) && camel_session_get_online (session)) {
167 ESourceRegistry *registry;
168 ESource *source;
169 GSettings *settings;
170 gboolean all_on_start;
171
172 settings = e_util_ref_settings ("org.gnome.evolution.mail");
173 all_on_start = g_settings_get_boolean (settings, "send-recv-all-on-start");
174 g_object_unref (settings);
175
176 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
177 source = e_source_registry_ref_source (registry, camel_service_get_uid (service));
178
179 if (source && e_source_has_extension (source, E_SOURCE_EXTENSION_REFRESH) &&
180 (all_on_start || e_source_refresh_get_enabled (e_source_get_extension (source, E_SOURCE_EXTENSION_REFRESH)))) {
181 e_source_refresh_force_timeout (source);
182 }
183
184 g_clear_object (&source);
185 }
186
187 g_clear_object (&session);
188 }
189
190 g_object_unref (activity);
191 }
192
193 static void
mail_backend_local_trash_expunge_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)194 mail_backend_local_trash_expunge_done_cb (GObject *source_object,
195 GAsyncResult *result,
196 gpointer user_data)
197 {
198 CamelFolder *folder = CAMEL_FOLDER (source_object);
199 EActivity *activity = user_data;
200 GError *local_error = NULL;
201
202 e_mail_folder_expunge_finish (folder, result, &local_error);
203
204 if (local_error != NULL) {
205 g_warning (
206 "%s: Failed to expunge local trash: %s",
207 G_STRFUNC, local_error->message);
208 g_error_free (local_error);
209 }
210
211 g_object_unref (activity);
212 }
213
214 static void
mail_backend_set_session_offline_cb(gpointer user_data,GObject * object)215 mail_backend_set_session_offline_cb (gpointer user_data,
216 GObject *object)
217 {
218 CamelSession *session = user_data;
219
220 g_return_if_fail (CAMEL_IS_SESSION (session));
221
222 camel_session_set_online (session, FALSE);
223 g_object_unref (session);
224 }
225
226 static void
mail_backend_prepare_for_offline_cb(EShell * shell,EActivity * activity,EMailBackend * backend)227 mail_backend_prepare_for_offline_cb (EShell *shell,
228 EActivity *activity,
229 EMailBackend *backend)
230 {
231 GtkWindow *window;
232 EMailSession *session;
233 EMailAccountStore *account_store;
234 EShellBackend *shell_backend;
235 GQueue queue = G_QUEUE_INIT;
236
237 shell_backend = E_SHELL_BACKEND (backend);
238
239 window = e_shell_get_active_window (shell);
240 session = e_mail_backend_get_session (backend);
241 account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (session));
242
243 if (!e_shell_get_network_available (shell)) {
244 camel_session_set_online (CAMEL_SESSION (session), FALSE);
245 camel_operation_cancel_all ();
246 }
247
248 if (e_shell_backend_is_started (shell_backend)) {
249 gboolean ask_to_synchronize;
250 gboolean synchronize = FALSE;
251 GCancellable *cancellable;
252
253 ask_to_synchronize =
254 e_shell_get_network_available (shell) &&
255 mail_backend_any_store_requires_downsync (account_store);
256
257 if (ask_to_synchronize) {
258 synchronize = e_util_prompt_user (
259 window, "org.gnome.evolution.mail", NULL, "mail:ask-quick-offline", NULL);
260 }
261
262 if (!synchronize) {
263 e_shell_backend_cancel_all (shell_backend);
264 camel_session_set_online (CAMEL_SESSION (session), FALSE);
265 }
266
267 cancellable = e_activity_get_cancellable (activity);
268 if (!cancellable) {
269 cancellable = camel_operation_new ();
270 e_activity_set_cancellable (activity, cancellable);
271 g_object_unref (cancellable);
272 } else {
273 /* Maybe the cancellable just got cancelled when the above
274 camel_operation_cancel_all() had been called, but we want
275 it alive for the following "go-offline" operation, thus reset it. */
276 g_cancellable_reset (cancellable);
277 }
278
279 e_shell_backend_add_activity (shell_backend, activity);
280 }
281
282 g_object_weak_ref (
283 G_OBJECT (activity),
284 mail_backend_set_session_offline_cb,
285 g_object_ref (session));
286
287 e_mail_account_store_queue_enabled_services (account_store, &queue);
288 while (!g_queue_is_empty (&queue)) {
289 CamelService *service;
290
291 service = g_queue_pop_head (&queue);
292
293 if (!CAMEL_IS_STORE (service))
294 continue;
295
296 e_mail_store_go_offline (
297 CAMEL_STORE (service), G_PRIORITY_DEFAULT,
298 e_activity_get_cancellable (activity),
299 (GAsyncReadyCallback) mail_backend_store_operation_done_cb,
300 g_object_ref (activity));
301 }
302 }
303
304 static void
mail_backend_prepare_for_online_cb(EShell * shell,EActivity * activity,EMailBackend * backend)305 mail_backend_prepare_for_online_cb (EShell *shell,
306 EActivity *activity,
307 EMailBackend *backend)
308 {
309 EMailSession *session;
310 EMailAccountStore *account_store;
311 GQueue queue = G_QUEUE_INIT;
312 GSettings *settings;
313 gboolean with_send_recv;
314
315 if (e_shell_backend_is_started (E_SHELL_BACKEND (backend))) {
316 if (!e_activity_get_cancellable (activity)) {
317 GCancellable *cancellable;
318
319 cancellable = camel_operation_new ();
320 e_activity_set_cancellable (activity, cancellable);
321 g_object_unref (cancellable);
322 }
323
324 e_shell_backend_add_activity (E_SHELL_BACKEND (backend), activity);
325 }
326
327 settings = e_util_ref_settings ("org.gnome.evolution.mail");
328 with_send_recv = g_settings_get_boolean (settings, "send-recv-on-start");
329 g_object_unref (settings);
330
331 session = e_mail_backend_get_session (backend);
332 account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (session));
333
334 camel_session_set_online (CAMEL_SESSION (session), TRUE);
335
336 e_mail_account_store_queue_enabled_services (account_store, &queue);
337 while (!g_queue_is_empty (&queue)) {
338 CamelService *service;
339
340 service = g_queue_pop_head (&queue);
341 if (service == NULL)
342 continue;
343
344 if (CAMEL_IS_STORE (service))
345 e_mail_store_go_online (
346 CAMEL_STORE (service), G_PRIORITY_DEFAULT,
347 e_activity_get_cancellable (activity),
348 (GAsyncReadyCallback) (with_send_recv ? mail_backend_store_go_online_done_cb : mail_backend_store_operation_done_cb),
349 g_object_ref (activity));
350 }
351 }
352
353 /* Helper for mail_backend_prepare_for_quit_cb() */
354 static void
mail_backend_delete_junk(CamelService * service,EMailBackend * backend)355 mail_backend_delete_junk (CamelService *service,
356 EMailBackend *backend)
357 {
358 CamelFolder *folder;
359 GPtrArray *uids;
360 guint32 flags;
361 guint32 mask;
362 guint ii;
363
364 /* FIXME camel_store_get_junk_folder_sync() may block. */
365 folder = camel_store_get_junk_folder_sync (
366 CAMEL_STORE (service), NULL, NULL);
367 if (folder == NULL)
368 return;
369
370 uids = camel_folder_get_uids (folder);
371 flags = mask = CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN;
372
373 camel_folder_freeze (folder);
374
375 for (ii = 0; ii < uids->len; ii++) {
376 const gchar *uid = uids->pdata[ii];
377 camel_folder_set_message_flags (folder, uid, flags, mask);
378 }
379
380 camel_folder_thaw (folder);
381
382 camel_folder_free_uids (folder, uids);
383 g_object_unref (folder);
384 }
385
386 /* Helper for mail_backend_prepare_for_quit_cb() */
387 static gboolean
mail_backend_poll_to_quit(gpointer user_data)388 mail_backend_poll_to_quit (gpointer user_data)
389 {
390 return mail_msg_active ();
391 }
392
393 static gboolean
mail_backend_service_is_enabled(ESourceRegistry * registry,CamelService * service)394 mail_backend_service_is_enabled (ESourceRegistry *registry,
395 CamelService *service)
396 {
397 const gchar *uid;
398 ESource *source;
399 gboolean enabled;
400
401 g_return_val_if_fail (registry != NULL, FALSE);
402 g_return_val_if_fail (service != NULL, FALSE);
403
404 uid = camel_service_get_uid (service);
405 g_return_val_if_fail (uid != NULL, FALSE);
406
407 source = e_source_registry_ref_source (registry, uid);
408 if (!source)
409 return FALSE;
410
411 enabled = e_source_registry_check_enabled (registry, source);
412 g_object_unref (source);
413
414 return enabled;
415 }
416
417 static void
mail_backend_prepare_for_quit_cb(EShell * shell,EActivity * activity,EMailBackend * backend)418 mail_backend_prepare_for_quit_cb (EShell *shell,
419 EActivity *activity,
420 EMailBackend *backend)
421 {
422 EMailSession *session;
423 ESourceRegistry *registry;
424 GList *list, *link;
425 GCancellable *cancellable;
426 gboolean delete_junk;
427 gboolean empty_trash;
428
429 session = e_mail_backend_get_session (backend);
430 registry = e_shell_get_registry (shell);
431
432 delete_junk = e_mail_backend_delete_junk_policy_decision (backend);
433 empty_trash = e_mail_backend_empty_trash_policy_decision (backend);
434
435 camel_application_is_exiting = TRUE;
436
437 camel_operation_cancel_all ();
438 mail_vfolder_shutdown ();
439
440 cancellable = e_activity_get_cancellable (activity);
441 if (cancellable) {
442 /* Maybe the cancellable just got cancelled when the above
443 camel_operation_cancel_all() had been called, but we want
444 it alive for the following operations, thus reset it. */
445 g_cancellable_reset (cancellable);
446 }
447
448 list = camel_session_list_services (CAMEL_SESSION (session));
449
450 if (delete_junk) {
451 for (link = list; link != NULL; link = g_list_next (link)) {
452 CamelService *service;
453
454 service = CAMEL_SERVICE (link->data);
455
456 if (!CAMEL_IS_STORE (service) ||
457 !mail_backend_service_is_enabled (registry, service))
458 continue;
459
460 mail_backend_delete_junk (service, backend);
461 }
462 }
463
464 for (link = list; link != NULL; link = g_list_next (link)) {
465 CamelService *service;
466 gboolean store_is_local;
467 const gchar *uid;
468
469 service = CAMEL_SERVICE (link->data);
470
471 if (!CAMEL_IS_STORE (service))
472 continue;
473
474 if (!mail_backend_service_is_enabled (registry, service))
475 continue;
476
477 uid = camel_service_get_uid (service);
478 store_is_local = g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID);
479
480 if (empty_trash && store_is_local) {
481 /* local trash requires special handling,
482 * due to POP3's "delete-expunged" option */
483 CamelFolder *local_trash;
484
485 /* This should be lightning-fast since
486 * it's just the local trash folder. */
487 local_trash = camel_store_get_trash_folder_sync (
488 CAMEL_STORE (service), cancellable, NULL);
489
490 if (local_trash != NULL) {
491 e_mail_folder_expunge (
492 local_trash,
493 G_PRIORITY_DEFAULT, cancellable,
494 mail_backend_local_trash_expunge_done_cb,
495 g_object_ref (activity));
496
497 g_object_unref (local_trash);
498 }
499 } else {
500 /* FIXME Not passing a GCancellable. */
501 /* FIXME This operation should be queued. */
502 camel_store_synchronize (
503 CAMEL_STORE (service),
504 empty_trash, G_PRIORITY_DEFAULT,
505 NULL, (GAsyncReadyCallback)
506 mail_backend_store_operation_done_cb,
507 g_object_ref (activity));
508 }
509 }
510
511 g_list_free_full (list, (GDestroyNotify) g_object_unref);
512
513 /* Now we poll until all activities are actually cancelled or finished.
514 * Reffing the activity delays quitting; the reference count
515 * acts like a counting semaphore. */
516 if (mail_msg_active ()) {
517 e_named_timeout_add_seconds_full (
518 G_PRIORITY_DEFAULT,
519 QUIT_POLL_INTERVAL,
520 mail_backend_poll_to_quit,
521 g_object_ref (activity),
522 (GDestroyNotify) g_object_unref);
523 }
524 }
525
526 static void
mail_backend_quit_requested_cb(EShell * shell,EShellQuitReason reason,EShellBackend * mail_shell_backend)527 mail_backend_quit_requested_cb (EShell *shell,
528 EShellQuitReason reason,
529 EShellBackend *mail_shell_backend)
530 {
531 EMailBackend *backend;
532 EMailSession *session;
533 CamelFolder *folder;
534 GtkWindow *window;
535 GList *app_windows;
536 gint response;
537
538 window = e_shell_get_active_window (shell);
539
540 /* We can quit immediately if offline. */
541 if (!e_shell_get_online (shell))
542 return;
543
544 /* Or if another Evolution process asked us to. */
545 if (reason == E_SHELL_QUIT_REMOTE_REQUEST)
546 return;
547
548 if (!e_shell_backend_is_started (mail_shell_backend))
549 return;
550
551 /* Check Outbox for any unsent messages. */
552
553 backend = E_MAIL_BACKEND (mail_shell_backend);
554 session = e_mail_backend_get_session (backend);
555
556 folder = e_mail_session_get_local_folder (
557 session, E_MAIL_LOCAL_FOLDER_OUTBOX);
558 if (folder == NULL)
559 return;
560
561 if (camel_folder_summary_get_visible_count (camel_folder_get_folder_summary (folder)) == 0)
562 return;
563
564 app_windows = gtk_application_get_windows (GTK_APPLICATION (shell));
565 while (app_windows) {
566 if (E_IS_SHELL_WINDOW (app_windows->data))
567 break;
568
569 app_windows = g_list_next (app_windows);
570 }
571
572 /* Either there is any EShellWindow available, then the quit can be
573 truly cancelled, or there is none and the question is useless. */
574 if (!app_windows)
575 return;
576
577 response = e_alert_run_dialog_for_args (
578 window, "mail:exit-unsent-question", NULL);
579
580 if (response == GTK_RESPONSE_YES)
581 return;
582
583 e_shell_cancel_quit (shell);
584 }
585
586 static void
mail_backend_folder_deleted_cb(MailFolderCache * folder_cache,CamelStore * store,const gchar * folder_name,EMailBackend * backend)587 mail_backend_folder_deleted_cb (MailFolderCache *folder_cache,
588 CamelStore *store,
589 const gchar *folder_name,
590 EMailBackend *backend)
591 {
592 EShell *shell;
593 CamelStoreClass *class;
594 ESourceRegistry *registry;
595 EShellBackend *shell_backend;
596 EMailSession *session;
597 EAlertSink *alert_sink;
598 GList *list, *link;
599 const gchar *extension_name;
600 const gchar *local_drafts_folder_uri;
601 const gchar *local_sent_folder_uri;
602 gchar *uri;
603
604 /* Check whether the deleted folder was a designated Drafts or
605 * Sent folder for any mail account, and if so revert the setting
606 * to the equivalent local folder, which is always present. */
607
608 shell_backend = E_SHELL_BACKEND (backend);
609 shell = e_shell_backend_get_shell (shell_backend);
610 registry = e_shell_get_registry (shell);
611
612 class = CAMEL_STORE_GET_CLASS (store);
613 g_return_if_fail (class->equal_folder_name != NULL);
614
615 session = e_mail_backend_get_session (backend);
616 alert_sink = e_mail_backend_get_alert_sink (backend);
617
618 local_drafts_folder_uri =
619 e_mail_session_get_local_folder_uri (
620 session, E_MAIL_LOCAL_FOLDER_DRAFTS);
621
622 local_sent_folder_uri =
623 e_mail_session_get_local_folder_uri (
624 session, E_MAIL_LOCAL_FOLDER_SENT);
625
626 uri = e_mail_folder_uri_build (store, folder_name);
627
628 extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
629 list = e_source_registry_list_sources (registry, extension_name);
630
631 for (link = list; link != NULL; link = g_list_next (link)) {
632 ESource *source = E_SOURCE (link->data);
633 ESourceExtension *extension;
634 const gchar *drafts_folder_uri;
635
636 extension = e_source_get_extension (source, extension_name);
637
638 drafts_folder_uri =
639 e_source_mail_composition_get_drafts_folder (
640 E_SOURCE_MAIL_COMPOSITION (extension));
641
642 if (!drafts_folder_uri)
643 continue;
644
645 if (class->equal_folder_name (drafts_folder_uri, uri)) {
646 GError *error = NULL;
647
648 e_source_mail_composition_set_drafts_folder (
649 E_SOURCE_MAIL_COMPOSITION (extension),
650 local_drafts_folder_uri);
651
652 /* FIXME This is a blocking D-Bus method call. */
653 if (!e_source_write_sync (source, NULL, &error)) {
654 g_warning ("%s", error->message);
655 g_error_free (error);
656 }
657 }
658 }
659
660 g_list_free_full (list, (GDestroyNotify) g_object_unref);
661
662 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
663 list = e_source_registry_list_sources (registry, extension_name);
664
665 for (link = list; link != NULL; link = g_list_next (link)) {
666 ESource *source = E_SOURCE (link->data);
667 ESourceMailSubmission *extension;
668 const gchar *sent_folder_uri;
669
670 extension = e_source_get_extension (source, extension_name);
671
672 sent_folder_uri = e_source_mail_submission_get_sent_folder (extension);
673
674 if (sent_folder_uri == NULL)
675 continue;
676
677 if (class->equal_folder_name (sent_folder_uri, uri)) {
678 GError *error = NULL;
679
680 e_source_mail_submission_set_sent_folder (extension, local_sent_folder_uri);
681
682 /* FIXME This is a blocking D-Bus method call. */
683 if (!e_source_write_sync (source, NULL, &error)) {
684 g_warning ("%s", error->message);
685 g_error_free (error);
686 }
687 }
688 }
689
690 g_list_free_full (list, (GDestroyNotify) g_object_unref);
691
692 g_free (uri);
693
694 /* This does something completely different.
695 * XXX Make it a separate signal handler? */
696 mail_filter_delete_folder (store, folder_name, alert_sink);
697 }
698
699 static void
mail_backend_folder_renamed_cb(MailFolderCache * folder_cache,CamelStore * store,const gchar * old_folder_name,const gchar * new_folder_name,EMailBackend * backend)700 mail_backend_folder_renamed_cb (MailFolderCache *folder_cache,
701 CamelStore *store,
702 const gchar *old_folder_name,
703 const gchar *new_folder_name,
704 EMailBackend *backend)
705 {
706 EShell *shell;
707 CamelStoreClass *class;
708 ESourceRegistry *registry;
709 EShellBackend *shell_backend;
710 GList *list, *link;
711 const gchar *extension_name;
712 gchar *old_uri;
713 gchar *new_uri;
714 gint ii;
715
716 const gchar *cachenames[] = {
717 "views/current_view-",
718 "views/custom_view-"
719 };
720
721 /* Check whether the renamed folder was a designated Drafts or
722 * Sent folder for any mail account, and if so update the setting
723 * to the new folder name. */
724
725 shell_backend = E_SHELL_BACKEND (backend);
726 shell = e_shell_backend_get_shell (shell_backend);
727 registry = e_shell_get_registry (shell);
728
729 class = CAMEL_STORE_GET_CLASS (store);
730 g_return_if_fail (class->equal_folder_name != NULL);
731
732 old_uri = e_mail_folder_uri_build (store, old_folder_name);
733 new_uri = e_mail_folder_uri_build (store, new_folder_name);
734
735 extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
736 list = e_source_registry_list_sources (registry, extension_name);
737
738 for (link = list; link != NULL; link = g_list_next (link)) {
739 ESource *source = E_SOURCE (link->data);
740 ESourceExtension *extension;
741 const gchar *drafts_folder_uri;
742 gboolean need_update;
743
744 extension = e_source_get_extension (source, extension_name);
745
746 drafts_folder_uri =
747 e_source_mail_composition_get_drafts_folder (
748 E_SOURCE_MAIL_COMPOSITION (extension));
749
750 need_update =
751 (drafts_folder_uri != NULL) &&
752 class->equal_folder_name (drafts_folder_uri, old_uri);
753
754 if (need_update) {
755 GError *error = NULL;
756
757 e_source_mail_composition_set_drafts_folder (
758 E_SOURCE_MAIL_COMPOSITION (extension),
759 new_uri);
760
761 /* FIXME This is a blocking D-Bus method call. */
762 if (!e_source_write_sync (source, NULL, &error)) {
763 g_warning ("%s", error->message);
764 g_error_free (error);
765 }
766 }
767 }
768
769 g_list_free_full (list, (GDestroyNotify) g_object_unref);
770
771 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
772 list = e_source_registry_list_sources (registry, extension_name);
773
774 for (link = list; link != NULL; link = g_list_next (link)) {
775 ESource *source = E_SOURCE (link->data);
776 ESourceMailSubmission *extension;
777 const gchar *sent_folder_uri;
778 gboolean need_update;
779
780 extension = e_source_get_extension (source, extension_name);
781
782 sent_folder_uri = e_source_mail_submission_get_sent_folder (extension);
783
784 need_update =
785 (sent_folder_uri != NULL) &&
786 class->equal_folder_name (sent_folder_uri, old_uri);
787
788 if (need_update) {
789 GError *error = NULL;
790
791 e_source_mail_submission_set_sent_folder (extension, new_uri);
792
793 /* FIXME This is a blocking D-Bus method call. */
794 if (!e_source_write_sync (source, NULL, &error)) {
795 g_warning ("%s", error->message);
796 g_error_free (error);
797 }
798 }
799 }
800
801 g_list_free_full (list, (GDestroyNotify) g_object_unref);
802
803 /* Rename GalView files. */
804
805 for (ii = 0; ii < G_N_ELEMENTS (cachenames); ii++) {
806 gchar *oldname;
807 gchar *newname;
808
809 oldname = mail_backend_uri_to_evname (old_uri, cachenames[ii]);
810 newname = mail_backend_uri_to_evname (new_uri, cachenames[ii]);
811
812 /* Ignore errors; doesn't matter. */
813 if (g_rename (oldname, newname) == -1 && errno != ENOENT) {
814 g_warning (
815 "%s: Failed to rename '%s' to '%s': %s",
816 G_STRFUNC,
817 oldname, newname,
818 g_strerror (errno));
819 }
820
821 g_free (oldname);
822 g_free (newname);
823 }
824
825 g_free (old_uri);
826 g_free (new_uri);
827
828 /* This does something completely different.
829 * XXX Make it a separate signal handler? */
830 mail_filter_rename_folder (
831 store, old_folder_name, new_folder_name);
832 }
833
834 static void
mail_backend_folder_changed_cb(MailFolderCache * folder_cache,CamelStore * store,const gchar * folder_name,gint new_messages,const gchar * msg_uid,const gchar * msg_sender,const gchar * msg_subject,EMailBackend * mail_backend)835 mail_backend_folder_changed_cb (MailFolderCache *folder_cache,
836 CamelStore *store,
837 const gchar *folder_name,
838 gint new_messages,
839 const gchar *msg_uid,
840 const gchar *msg_sender,
841 const gchar *msg_subject,
842 EMailBackend *mail_backend)
843 {
844 EMEvent *event = em_event_peek ();
845 EMEventTargetFolder *target;
846 EMFolderTreeModel *model;
847 CamelFolder *folder;
848 gchar *folder_uri;
849 gint folder_type;
850 CamelFolderInfoFlags flags = 0;
851
852 folder_uri = e_mail_folder_uri_build (store, folder_name);
853
854 mail_folder_cache_get_folder_info_flags (
855 folder_cache, store, folder_name, &flags);
856
857 target = em_event_target_new_folder (
858 event, store, folder_uri, new_messages,
859 msg_uid, msg_sender, msg_subject);
860
861 g_free (folder_uri);
862
863 folder_type = (flags & CAMEL_FOLDER_TYPE_MASK);
864 target->is_inbox = (folder_type == CAMEL_FOLDER_TYPE_INBOX);
865
866 model = em_folder_tree_model_get_default ();
867 target->display_name = em_folder_tree_model_get_folder_name (
868 model, store, folder_name);
869
870 folder = mail_folder_cache_ref_folder (folder_cache, store, folder_name);
871 if (folder) {
872 target->full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
873 g_clear_object (&folder);
874 }
875
876 if (target->new > 0) {
877 EShell *shell;
878 EShellBackend *shell_backend;
879
880 shell_backend = E_SHELL_BACKEND (mail_backend);
881 shell = e_shell_backend_get_shell (shell_backend);
882 e_shell_event (shell, "mail-icon", (gpointer) "mail-unread");
883 }
884
885 /**
886 * @Event: folder.changed
887 * @Title: Folder changed
888 * @Target: EMEventTargetFolder
889 *
890 * folder.changed is emitted whenever a folder changes. There is no
891 * detail on how the folder has changed.
892 *
893 * UPDATE: We tell the number of new UIDs added rather than the new
894 * mails received.
895 */
896 e_event_emit (
897 (EEvent *) event, "folder.changed",
898 (EEventTarget *) target);
899 }
900
901 static void
mail_backend_folder_unread_updated_cb(MailFolderCache * folder_cache,CamelStore * store,const gchar * folder_name,gint unread_messages,EMailBackend * mail_backend)902 mail_backend_folder_unread_updated_cb (MailFolderCache *folder_cache,
903 CamelStore *store,
904 const gchar *folder_name,
905 gint unread_messages,
906 EMailBackend *mail_backend)
907 {
908 EMEvent *event = em_event_peek ();
909 EMEventTargetFolderUnread *target;
910 gchar *folder_uri;
911 gint folder_type;
912 CamelFolderInfoFlags flags = 0;
913
914 folder_uri = e_mail_folder_uri_build (store, folder_name);
915
916 mail_folder_cache_get_folder_info_flags (
917 folder_cache, store, folder_name, &flags);
918
919 target = em_event_target_new_folder_unread (
920 event, store, folder_uri, unread_messages);
921
922 g_free (folder_uri);
923
924 folder_type = (flags & CAMEL_FOLDER_TYPE_MASK);
925 target->is_inbox = (folder_type == CAMEL_FOLDER_TYPE_INBOX);
926
927 /**
928 * @Event: folder.unread-updated
929 * @Title: Folder unread updated
930 * @Target: EMEventTargetFolder
931 *
932 * folder.unread-updated is emitted whenever the number of unread messages
933 * in a folder changes.
934 */
935 e_event_emit (
936 (EEvent *) event, "folder.unread-updated",
937 (EEventTarget *) target);
938 }
939
940 static void
mail_backend_job_started_cb(CamelSession * session,GCancellable * cancellable,EShellBackend * shell_backend)941 mail_backend_job_started_cb (CamelSession *session,
942 GCancellable *cancellable,
943 EShellBackend *shell_backend)
944 {
945 EMailBackendPrivate *priv;
946 EActivity *activity;
947
948 priv = E_MAIL_BACKEND_GET_PRIVATE (shell_backend);
949
950 activity = e_activity_new ();
951 e_activity_set_cancellable (activity, cancellable);
952 e_shell_backend_add_activity (shell_backend, activity);
953
954 /* The hash table takes ownership of the activity. */
955 g_hash_table_insert (priv->jobs, cancellable, activity);
956 }
957
958 static void
mail_backend_job_finished_cb(CamelSession * session,GCancellable * cancellable,const GError * error,EShellBackend * shell_backend)959 mail_backend_job_finished_cb (CamelSession *session,
960 GCancellable *cancellable,
961 const GError *error,
962 EShellBackend *shell_backend)
963 {
964 EMailBackendPrivate *priv;
965 EShellBackendClass *class;
966 EActivity *activity;
967 const gchar *description;
968
969 priv = E_MAIL_BACKEND_GET_PRIVATE (shell_backend);
970 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
971
972 activity = g_hash_table_lookup (priv->jobs, cancellable);
973 description = e_activity_get_text (activity);
974
975 if (e_activity_handle_cancellation (activity, error)) {
976 /* nothing to do */
977
978 } else if (error != NULL) {
979 EShell *shell;
980 GtkApplication *application;
981 GList *list, *iter;
982
983 shell = e_shell_backend_get_shell (shell_backend);
984
985 application = GTK_APPLICATION (shell);
986 list = gtk_application_get_windows (application);
987
988 /* Submit the error to an appropriate EAlertSink. */
989 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
990 EShellView *shell_view;
991 EShellContent *shell_content;
992
993 if (!E_IS_SHELL_WINDOW (iter->data))
994 continue;
995
996 shell_view = e_shell_window_peek_shell_view (
997 E_SHELL_WINDOW (iter->data), class->name);
998
999 if (!E_IS_SHELL_VIEW (shell_view))
1000 continue;
1001
1002 shell_content =
1003 e_shell_view_get_shell_content (shell_view);
1004
1005 if (description != NULL && *description != '\0')
1006 e_alert_submit (
1007 E_ALERT_SINK (shell_content),
1008 "mail:async-error", description,
1009 error->message, NULL);
1010 else
1011 e_alert_submit (
1012 E_ALERT_SINK (shell_content),
1013 "mail:async-error-nodescribe",
1014 error->message, NULL);
1015
1016 break;
1017 }
1018 }
1019
1020 g_hash_table_remove (priv->jobs, cancellable);
1021 }
1022
1023 static void
mail_backend_allow_auth_prompt_cb(EMailSession * session,ESource * source,EShell * shell)1024 mail_backend_allow_auth_prompt_cb (EMailSession *session,
1025 ESource *source,
1026 EShell *shell)
1027 {
1028 g_return_if_fail (E_IS_SOURCE (source));
1029 g_return_if_fail (E_IS_SHELL (shell));
1030
1031 e_shell_allow_auth_prompt_for (shell, source);
1032 }
1033
1034 static void
mail_backend_connect_store_cb(EMailSession * session,CamelStore * store,gpointer user_data)1035 mail_backend_connect_store_cb (EMailSession *session,
1036 CamelStore *store,
1037 gpointer user_data)
1038 {
1039 EMailBackend *mail_backend = user_data;
1040 GCancellable *cancellable;
1041 EActivity *activity;
1042 GSettings *settings;
1043 gboolean with_send_recv;
1044 gchar *description;
1045
1046 g_return_if_fail (E_IS_MAIL_SESSION (session));
1047 g_return_if_fail (E_IS_MAIL_BACKEND (mail_backend));
1048 g_return_if_fail (CAMEL_IS_STORE (store));
1049
1050 settings = e_util_ref_settings ("org.gnome.evolution.mail");
1051 with_send_recv = g_settings_get_boolean (settings, "send-recv-on-start");
1052 g_object_unref (settings);
1053
1054 cancellable = camel_operation_new ();
1055 description = g_strdup_printf (_("Reconnecting to “%s”"), camel_service_get_display_name (CAMEL_SERVICE (store)));
1056
1057 activity = e_activity_new ();
1058 e_activity_set_cancellable (activity, cancellable);
1059 e_activity_set_text (activity, description);
1060
1061 if (E_IS_MAIL_UI_SESSION (session))
1062 e_mail_ui_session_add_activity (E_MAIL_UI_SESSION (session), activity);
1063
1064 e_mail_store_go_online (
1065 store, G_PRIORITY_DEFAULT,
1066 e_activity_get_cancellable (activity),
1067 (GAsyncReadyCallback) (with_send_recv ? mail_backend_store_go_online_done_cb : mail_backend_store_operation_done_cb),
1068 activity); /* Takes ownership of 'activity' */
1069
1070 g_object_unref (cancellable);
1071 g_free (description);
1072 }
1073
1074 static void
mail_backend_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1075 mail_backend_get_property (GObject *object,
1076 guint property_id,
1077 GValue *value,
1078 GParamSpec *pspec)
1079 {
1080 switch (property_id) {
1081 case PROP_SESSION:
1082 g_value_set_object (
1083 value,
1084 e_mail_backend_get_session (
1085 E_MAIL_BACKEND (object)));
1086 return;
1087
1088 case PROP_SEND_ACCOUNT_OVERRIDE:
1089 g_value_set_object (
1090 value,
1091 e_mail_backend_get_send_account_override (
1092 E_MAIL_BACKEND (object)));
1093 return;
1094
1095 case PROP_REMOTE_CONTENT:
1096 g_value_set_object (
1097 value,
1098 e_mail_backend_get_remote_content (
1099 E_MAIL_BACKEND (object)));
1100 return;
1101
1102 case PROP_MAIL_PROPERTIES:
1103 g_value_set_object (
1104 value,
1105 e_mail_backend_get_mail_properties (
1106 E_MAIL_BACKEND (object)));
1107 return;
1108 }
1109
1110 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1111 }
1112
1113 static void
mail_backend_dispose(GObject * object)1114 mail_backend_dispose (GObject *object)
1115 {
1116 EMailBackendPrivate *priv;
1117
1118 priv = E_MAIL_BACKEND_GET_PRIVATE (object);
1119
1120 if (priv->session != NULL) {
1121 em_folder_tree_model_free_default ();
1122
1123 g_signal_handlers_disconnect_matched (
1124 priv->session, G_SIGNAL_MATCH_DATA,
1125 0, 0, NULL, NULL, object);
1126 camel_session_remove_services (
1127 CAMEL_SESSION (priv->session));
1128 g_object_unref (priv->session);
1129 priv->session = NULL;
1130 }
1131
1132 /* There should be no unfinished jobs left. */
1133 g_warn_if_fail (g_hash_table_size (priv->jobs) == 0);
1134
1135 /* Chain up to parent's dispose() method. */
1136 G_OBJECT_CLASS (e_mail_backend_parent_class)->dispose (object);
1137 }
1138
1139 static void
mail_backend_finalize(GObject * object)1140 mail_backend_finalize (GObject *object)
1141 {
1142 EMailBackendPrivate *priv;
1143
1144 priv = E_MAIL_BACKEND_GET_PRIVATE (object);
1145
1146 g_hash_table_destroy (priv->jobs);
1147 g_clear_object (&priv->send_account_override);
1148 g_clear_object (&priv->remote_content);
1149 g_clear_object (&priv->mail_properties);
1150
1151 /* Chain up to parent's finalize() method. */
1152 G_OBJECT_CLASS (e_mail_backend_parent_class)->finalize (object);
1153
1154 camel_shutdown ();
1155 }
1156
1157 static void
mail_backend_add_store(EMailSession * session,CamelStore * store,EMailBackend * backend)1158 mail_backend_add_store (EMailSession *session,
1159 CamelStore *store,
1160 EMailBackend *backend)
1161 {
1162 EMFolderTreeModel *model;
1163
1164 model = em_folder_tree_model_get_default ();
1165 em_folder_tree_model_add_store (model, store);
1166 }
1167
1168 static void
mail_backend_remove_store(EMailSession * session,CamelStore * store,EMailBackend * backend)1169 mail_backend_remove_store (EMailSession *session,
1170 CamelStore *store,
1171 EMailBackend *backend)
1172 {
1173 EMFolderTreeModel *model;
1174
1175 model = em_folder_tree_model_get_default ();
1176 em_folder_tree_model_remove_store (model, store);
1177 }
1178
1179 #define SET_ACTIVITY(cancellable, activity) \
1180 g_object_set_data (G_OBJECT (cancellable), "e-activity", activity)
1181 #define GET_ACTIVITY(cancellable) \
1182 g_object_get_data (G_OBJECT (cancellable), "e-activity")
1183
1184 static void
mail_mt_create_activity(GCancellable * cancellable)1185 mail_mt_create_activity (GCancellable *cancellable)
1186 {
1187 EActivity *activity;
1188
1189 activity = e_activity_new ();
1190 e_activity_set_percent (activity, 0.0);
1191 e_activity_set_cancellable (activity, cancellable);
1192 SET_ACTIVITY (cancellable, activity);
1193 }
1194
1195 static void
mail_mt_submit_activity(GCancellable * cancellable)1196 mail_mt_submit_activity (GCancellable *cancellable)
1197 {
1198 EShell *shell;
1199 EShellBackend *shell_backend;
1200 EActivity *activity;
1201
1202 shell = e_shell_get_default ();
1203 shell_backend = e_shell_get_backend_by_name (
1204 shell, "mail");
1205
1206 activity = GET_ACTIVITY (cancellable);
1207 if (activity)
1208 e_shell_backend_add_activity (shell_backend, activity);
1209
1210 }
1211
1212 static void
mail_mt_free_activity(GCancellable * cancellable)1213 mail_mt_free_activity (GCancellable *cancellable)
1214 {
1215 EActivity *activity = GET_ACTIVITY (cancellable);
1216
1217 if (activity)
1218 g_object_unref (activity);
1219 }
1220
1221 static void
mail_mt_complete_activity(GCancellable * cancellable)1222 mail_mt_complete_activity (GCancellable *cancellable)
1223 {
1224 EActivity *activity = GET_ACTIVITY (cancellable);
1225
1226 if (activity)
1227 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
1228 }
1229
1230 static void
mail_mt_cancel_activity(GCancellable * cancellable)1231 mail_mt_cancel_activity (GCancellable *cancellable)
1232 {
1233 EActivity *activity = GET_ACTIVITY (cancellable);
1234
1235 if (activity)
1236 e_activity_set_state (activity, E_ACTIVITY_CANCELLED);
1237 }
1238
1239 static void
mail_mt_alert_error(GCancellable * cancellable,const gchar * what,const gchar * message)1240 mail_mt_alert_error (GCancellable *cancellable,
1241 const gchar *what,
1242 const gchar *message)
1243 {
1244 EShell *shell;
1245 EShellView *shell_view;
1246 EShellWindow *shell_window = NULL;
1247 EShellContent *shell_content;
1248 GList *list, *iter;
1249 GtkApplication *application;
1250
1251 shell = e_shell_get_default ();
1252 application = GTK_APPLICATION (shell);
1253 list = gtk_application_get_windows (application);
1254
1255 /* Find the most recently used EShellWindow. */
1256 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
1257 if (E_IS_SHELL_WINDOW (iter->data)) {
1258 shell_window = E_SHELL_WINDOW (iter->data);
1259 break;
1260 }
1261 }
1262
1263 /* If we can't find an EShellWindow then... well, screw it. */
1264 if (shell_window == NULL)
1265 return;
1266
1267 shell_view = e_shell_window_get_shell_view (
1268 shell_window, "mail");
1269 shell_content = e_shell_view_get_shell_content (shell_view);
1270
1271 if (what) {
1272 e_alert_submit (
1273 E_ALERT_SINK (shell_content),
1274 "mail:async-error", what,
1275 message, NULL);
1276 } else
1277 e_alert_submit (
1278 E_ALERT_SINK (shell_content),
1279 "mail:async-error-nodescribe",
1280 message, NULL);
1281 }
1282
1283 static EAlertSink *
mail_mt_get_alert_sink()1284 mail_mt_get_alert_sink ()
1285 {
1286 EShell *shell;
1287 EShellBackend *shell_backend;
1288
1289 shell = e_shell_get_default ();
1290 shell_backend = e_shell_get_backend_by_name (
1291 shell, "mail");
1292
1293 return e_mail_backend_get_alert_sink (E_MAIL_BACKEND (shell_backend));
1294 }
1295
1296 static void
mail_backend_constructed(GObject * object)1297 mail_backend_constructed (GObject *object)
1298 {
1299 EMailBackendPrivate *priv;
1300 EShell *shell;
1301 EShellBackend *shell_backend;
1302 MailFolderCache *folder_cache;
1303 ESourceRegistry *registry;
1304 gchar *config_filename;
1305 GList *providers;
1306
1307 priv = E_MAIL_BACKEND_GET_PRIVATE (object);
1308
1309 shell_backend = E_SHELL_BACKEND (object);
1310 shell = e_shell_backend_get_shell (shell_backend);
1311
1312 if (camel_init (e_get_user_data_dir (), TRUE) != 0)
1313 exit (0);
1314
1315 providers = camel_provider_list (TRUE);
1316 if (!providers) {
1317 g_warning ("%s: No camel providers loaded, exiting...", G_STRFUNC);
1318 exit (1);
1319 }
1320
1321 g_list_free (providers);
1322
1323 registry = e_shell_get_registry (shell);
1324 priv->session = e_mail_ui_session_new (registry);
1325
1326 g_signal_connect (
1327 priv->session, "allow-auth-prompt",
1328 G_CALLBACK (mail_backend_allow_auth_prompt_cb), shell);
1329
1330 g_signal_connect (
1331 priv->session, "flush-outbox",
1332 G_CALLBACK (mail_send), priv->session);
1333
1334 g_signal_connect (
1335 priv->session, "connect-store",
1336 G_CALLBACK (mail_backend_connect_store_cb), object);
1337
1338 /* Propagate "activity-added" signals from
1339 * the mail session to the shell backend. */
1340 g_signal_connect_swapped (
1341 priv->session, "activity-added",
1342 G_CALLBACK (e_shell_backend_add_activity),
1343 shell_backend);
1344
1345 g_signal_connect (
1346 priv->session, "job-started",
1347 G_CALLBACK (mail_backend_job_started_cb),
1348 shell_backend);
1349
1350 g_signal_connect (
1351 priv->session, "job-finished",
1352 G_CALLBACK (mail_backend_job_finished_cb),
1353 shell_backend);
1354
1355 g_signal_connect (
1356 priv->session, "store-added",
1357 G_CALLBACK (mail_backend_add_store),
1358 shell_backend);
1359
1360 g_signal_connect (
1361 priv->session, "store-removed",
1362 G_CALLBACK (mail_backend_remove_store),
1363 shell_backend);
1364
1365 g_signal_connect (
1366 shell, "prepare-for-offline",
1367 G_CALLBACK (mail_backend_prepare_for_offline_cb),
1368 shell_backend);
1369
1370 g_signal_connect (
1371 shell, "prepare-for-online",
1372 G_CALLBACK (mail_backend_prepare_for_online_cb),
1373 shell_backend);
1374
1375 g_signal_connect (
1376 shell, "prepare-for-quit",
1377 G_CALLBACK (mail_backend_prepare_for_quit_cb),
1378 shell_backend);
1379
1380 g_signal_connect (
1381 shell, "quit-requested",
1382 G_CALLBACK (mail_backend_quit_requested_cb),
1383 shell_backend);
1384
1385 folder_cache = e_mail_session_get_folder_cache (priv->session);
1386
1387 g_signal_connect (
1388 folder_cache, "folder-deleted",
1389 G_CALLBACK (mail_backend_folder_deleted_cb),
1390 shell_backend);
1391
1392 g_signal_connect (
1393 folder_cache, "folder-renamed",
1394 G_CALLBACK (mail_backend_folder_renamed_cb),
1395 shell_backend);
1396
1397 g_signal_connect (
1398 folder_cache, "folder-changed",
1399 G_CALLBACK (mail_backend_folder_changed_cb), shell_backend);
1400
1401 g_signal_connect (
1402 folder_cache, "folder-unread-updated",
1403 G_CALLBACK (mail_backend_folder_unread_updated_cb),
1404 shell_backend);
1405
1406 mail_config_init (priv->session);
1407
1408 mail_msg_register_activities (
1409 mail_mt_create_activity,
1410 mail_mt_submit_activity,
1411 mail_mt_free_activity,
1412 mail_mt_complete_activity,
1413 mail_mt_cancel_activity,
1414 mail_mt_alert_error,
1415 mail_mt_get_alert_sink);
1416
1417 /* Chain up to parent's constructed() method. */
1418 G_OBJECT_CLASS (e_mail_backend_parent_class)->constructed (object);
1419
1420 config_filename = g_build_filename (e_shell_backend_get_config_dir (shell_backend), "send-overrides.ini", NULL);
1421 priv->send_account_override = e_mail_send_account_override_new (config_filename);
1422 g_free (config_filename);
1423
1424 config_filename = g_build_filename (e_shell_backend_get_config_dir (shell_backend), "remote-content.db", NULL);
1425 priv->remote_content = e_mail_remote_content_new (config_filename);
1426 g_free (config_filename);
1427
1428 config_filename = g_build_filename (e_shell_backend_get_config_dir (shell_backend), "properties.db", NULL);
1429 priv->mail_properties = e_mail_properties_new (config_filename);
1430 g_free (config_filename);
1431 }
1432
1433 static void
e_mail_backend_class_init(EMailBackendClass * class)1434 e_mail_backend_class_init (EMailBackendClass *class)
1435 {
1436 GObjectClass *object_class;
1437 EShellBackendClass *shell_backend_class;
1438
1439 g_type_class_add_private (class, sizeof (EMailBackendPrivate));
1440
1441 object_class = G_OBJECT_CLASS (class);
1442 object_class->get_property = mail_backend_get_property;
1443 object_class->dispose = mail_backend_dispose;
1444 object_class->finalize = mail_backend_finalize;
1445 object_class->constructed = mail_backend_constructed;
1446
1447 shell_backend_class = E_SHELL_BACKEND_CLASS (class);
1448 shell_backend_class->migrate = e_mail_migrate;
1449 shell_backend_class->get_data_dir = mail_shell_backend_get_data_dir;
1450 shell_backend_class->get_config_dir = mail_shell_backend_get_config_dir;
1451
1452 g_object_class_install_property (
1453 object_class,
1454 PROP_SESSION,
1455 g_param_spec_object (
1456 "session",
1457 NULL,
1458 NULL,
1459 E_TYPE_MAIL_SESSION,
1460 G_PARAM_READABLE));
1461
1462 g_object_class_install_property (
1463 object_class,
1464 PROP_SEND_ACCOUNT_OVERRIDE,
1465 g_param_spec_object (
1466 "send-account-override",
1467 NULL,
1468 NULL,
1469 E_TYPE_MAIL_SEND_ACCOUNT_OVERRIDE,
1470 G_PARAM_READABLE));
1471
1472 g_object_class_install_property (
1473 object_class,
1474 PROP_REMOTE_CONTENT,
1475 g_param_spec_object (
1476 "remote-content",
1477 NULL,
1478 NULL,
1479 E_TYPE_MAIL_REMOTE_CONTENT,
1480 G_PARAM_READABLE));
1481
1482 g_object_class_install_property (
1483 object_class,
1484 PROP_MAIL_PROPERTIES,
1485 g_param_spec_object (
1486 "mail-properties",
1487 NULL,
1488 NULL,
1489 E_TYPE_MAIL_PROPERTIES,
1490 G_PARAM_READABLE));
1491 }
1492
1493 static void
e_mail_backend_init(EMailBackend * backend)1494 e_mail_backend_init (EMailBackend *backend)
1495 {
1496 backend->priv = E_MAIL_BACKEND_GET_PRIVATE (backend);
1497
1498 backend->priv->jobs = g_hash_table_new_full (
1499 (GHashFunc) g_direct_hash,
1500 (GEqualFunc) g_direct_equal,
1501 (GDestroyNotify) NULL,
1502 (GDestroyNotify) g_object_unref);
1503 }
1504
1505 EMailSession *
e_mail_backend_get_session(EMailBackend * backend)1506 e_mail_backend_get_session (EMailBackend *backend)
1507 {
1508 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
1509
1510 return backend->priv->session;
1511 }
1512
1513 EAlertSink *
e_mail_backend_get_alert_sink(EMailBackend * backend)1514 e_mail_backend_get_alert_sink (EMailBackend *backend)
1515 {
1516 EShell *shell;
1517 EShellView *shell_view;
1518 EShellBackend *shell_backend;
1519 EShellContent *shell_content;
1520 EShellWindow *shell_window = NULL;
1521 EShellBackendClass *class;
1522 GtkApplication *application;
1523 GList *list, *link;
1524
1525 /* XXX This is meant to be a convenient but temporary hack.
1526 * It digs through the list of available EShellWindows
1527 * to find a suitable EAlertSink. */
1528
1529 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
1530
1531 shell_backend = E_SHELL_BACKEND (backend);
1532 shell = e_shell_backend_get_shell (shell_backend);
1533
1534 application = GTK_APPLICATION (shell);
1535 list = gtk_application_get_windows (application);
1536
1537 /* Find the most recently used EShellWindow. */
1538 for (link = list; link != NULL; link = g_list_next (link)) {
1539 if (E_IS_SHELL_WINDOW (link->data)) {
1540 shell_window = E_SHELL_WINDOW (link->data);
1541 break;
1542 }
1543 }
1544
1545 g_return_val_if_fail (shell_window != NULL, NULL);
1546
1547 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
1548 shell_view = e_shell_window_get_shell_view (shell_window, class->name);
1549 shell_content = e_shell_view_get_shell_content (shell_view);
1550
1551 return E_ALERT_SINK (shell_content);
1552 }
1553
1554 gboolean
e_mail_backend_delete_junk_policy_decision(EMailBackend * backend)1555 e_mail_backend_delete_junk_policy_decision (EMailBackend *backend)
1556 {
1557 EMailBackendClass *class;
1558
1559 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), FALSE);
1560
1561 class = E_MAIL_BACKEND_GET_CLASS (backend);
1562 g_return_val_if_fail (class != NULL, FALSE);
1563
1564 if (class->delete_junk_policy_decision == NULL)
1565 return FALSE;
1566
1567 return class->delete_junk_policy_decision (backend);
1568 }
1569
1570 gboolean
e_mail_backend_empty_trash_policy_decision(EMailBackend * backend)1571 e_mail_backend_empty_trash_policy_decision (EMailBackend *backend)
1572 {
1573 EMailBackendClass *class;
1574
1575 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), FALSE);
1576
1577 class = E_MAIL_BACKEND_GET_CLASS (backend);
1578 g_return_val_if_fail (class != NULL, FALSE);
1579
1580 if (class->empty_trash_policy_decision == NULL)
1581 return FALSE;
1582
1583 return class->empty_trash_policy_decision (backend);
1584 }
1585
1586 EMailSendAccountOverride *
e_mail_backend_get_send_account_override(EMailBackend * backend)1587 e_mail_backend_get_send_account_override (EMailBackend *backend)
1588 {
1589 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
1590
1591 return backend->priv->send_account_override;
1592 }
1593
1594 EMailRemoteContent *
e_mail_backend_get_remote_content(EMailBackend * backend)1595 e_mail_backend_get_remote_content (EMailBackend *backend)
1596 {
1597 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
1598
1599 return backend->priv->remote_content;
1600 }
1601
1602 EMailProperties *
e_mail_backend_get_mail_properties(EMailBackend * backend)1603 e_mail_backend_get_mail_properties (EMailBackend *backend)
1604 {
1605 g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
1606
1607 return backend->priv->mail_properties;
1608 }
1609