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