evolution-3.6.4/mail/e-mail-backend.c

No issues found

Incomplete coverage

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