evolution-3.6.4/libemail-engine/e-mail-session.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-mail-session.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-session.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-session.c
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  *
  18  * Authors:
  19  *   Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
  20  *
  21  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  22  * Copyright (C) 2009 Intel Corporation
  23  *
  24  */
  25 
  26 /* mail-session.c: handles the session information and resource manipulation */
  27 
  28 #ifdef HAVE_CONFIG_H
  29 #include <config.h>
  30 #endif
  31 
  32 #include <errno.h>
  33 #include <stdlib.h>
  34 #include <string.h>
  35 
  36 #include <glib/gi18n.h>
  37 #include <glib/gstdio.h>
  38 
  39 #include <gtk/gtk.h>
  40 
  41 #ifdef HAVE_CANBERRA
  42 #include <canberra-gtk.h>
  43 #endif
  44 
  45 #include <libebackend/libebackend.h>
  46 #include <libedataserverui/libedataserverui.h>
  47 
  48 #include "libemail-utils/mail-mt.h"
  49 
  50 /* This is our hack, not part of libcamel. */
  51 #include "camel-null-store.h"
  52 
  53 #include "e-mail-authenticator.h"
  54 #include "e-mail-junk-filter.h"
  55 #include "e-mail-session.h"
  56 #include "e-mail-folder-utils.h"
  57 #include "e-mail-utils.h"
  58 #include "mail-config.h"
  59 #include "mail-ops.h"
  60 #include "mail-tools.h"
  61 
  62 #define E_MAIL_SESSION_GET_PRIVATE(obj) \
  63 	(G_TYPE_INSTANCE_GET_PRIVATE \
  64 	((obj), E_TYPE_MAIL_SESSION, EMailSessionPrivate))
  65 
  66 typedef struct _AsyncContext AsyncContext;
  67 
  68 struct _EMailSessionPrivate {
  69 	MailFolderCache *folder_cache;
  70 	ESourceRegistry *registry;
  71 
  72 	/* ESource UID -> Timeout ID */
  73 	GHashTable *auto_refresh_table;
  74 
  75 	gulong source_added_handler_id;
  76 	gulong source_removed_handler_id;
  77 	gulong source_enabled_handler_id;
  78 	gulong source_disabled_handler_id;
  79 	gulong default_mail_account_handler_id;
  80 
  81 	CamelService *local_store;
  82 	CamelService *vfolder_store;
  83 
  84 	FILE *filter_logfile;
  85 	GHashTable *junk_filters;
  86 	EProxy *proxy;
  87 
  88 	/* Local folder cache. */
  89 	GPtrArray *local_folders;
  90 	GPtrArray *local_folder_uris;
  91 
  92 	guint preparing_flush;
  93 	GMutex *preparing_flush_lock;
  94 };
  95 
  96 struct _AsyncContext {
  97 	/* arguments */
  98 	CamelStoreGetFolderFlags flags;
  99 	gchar *uid;
 100 	gchar *uri;
 101 
 102 	/* results */
 103 	CamelFolder *folder;
 104 };
 105 
 106 enum {
 107 	PROP_0,
 108 	PROP_FOLDER_CACHE,
 109 	PROP_JUNK_FILTER_NAME,
 110 	PROP_LOCAL_STORE,
 111 	PROP_REGISTRY,
 112 	PROP_VFOLDER_STORE
 113 };
 114 
 115 static const gchar *local_folder_names[E_MAIL_NUM_LOCAL_FOLDERS] = {
 116 	N_("Inbox"),		/* E_MAIL_LOCAL_FOLDER_INBOX */
 117 	N_("Drafts"),		/* E_MAIL_LOCAL_FOLDER_DRAFTS */
 118 	N_("Outbox"),		/* E_MAIL_LOCAL_FOLDER_OUTBOX */
 119 	N_("Sent"),		/* E_MAIL_LOCAL_FOLDER_SENT */
 120 	N_("Templates"),	/* E_MAIL_LOCAL_FOLDER_TEMPLATES */
 121 	"Inbox"			/* E_MAIL_LOCAL_FOLDER_LOCAL_INBOX */
 122 };
 123 
 124 enum {
 125 	FLUSH_OUTBOX,
 126 	REFRESH_SERVICE,
 127 	STORE_ADDED,
 128 	STORE_REMOVED,
 129 	LAST_SIGNAL
 130 };
 131 
 132 static guint signals[LAST_SIGNAL];
 133 
 134 static gchar *mail_data_dir;
 135 static gchar *mail_cache_dir;
 136 static gchar *mail_config_dir;
 137 
 138 G_DEFINE_TYPE_WITH_CODE (
 139 	EMailSession,
 140 	e_mail_session,
 141 	CAMEL_TYPE_SESSION,
 142 	G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
 143 
 144 /* Support for CamelSession.alert_user() *************************************/
 145 
 146 static GQueue user_message_queue = { NULL, NULL, 0 };
 147 
 148 struct _user_message_msg {
 149 	MailMsg base;
 150 
 151 	CamelSessionAlertType type;
 152 	gchar *prompt;
 153 	GSList *button_captions;
 154 	EFlag *done;
 155 
 156 	gint result;
 157 	guint ismain : 1;
 158 };
 159 
 160 static void user_message_exec (struct _user_message_msg *m,
 161                                GCancellable *cancellable,
 162                                GError **error);
 163 
 164 static void
 165 user_message_response_free (struct _user_message_msg *m)
 166 {
 167 
 168 	/* check for pendings */
 169 	if (!g_queue_is_empty (&user_message_queue)) {
 170 		GCancellable *cancellable;
 171 
 172 		m = g_queue_pop_head (&user_message_queue);
 173 		cancellable = m->base.cancellable;
 174 		user_message_exec (m, cancellable, &m->base.error);
 175 		mail_msg_unref (m);
 176 	}
 177 }
 178 
 179 /* clicked, send back the reply */
 180 static void
 181 user_message_response (struct _user_message_msg *m)
 182 {
 183 	/* if !allow_cancel, then we've already replied */
 184 	if (m->button_captions) {
 185 		m->result = TRUE; //If Accepted
 186 		e_flag_set (m->done);
 187 	}
 188 
 189 	user_message_response_free (m);
 190 }
 191 
 192 static void
 193 user_message_exec (struct _user_message_msg *m,
 194                    GCancellable *cancellable,
 195                    GError **error)
 196 {
 197 	/* XXX This is a case where we need to be able to construct
 198 	 *     custom EAlerts without a predefined XML definition. */
 199 	if (m->ismain) {
 200 		/* Use DBUS to raise dialogs in clients and reply back.
 201 		 * For now say accept all. */
 202 		user_message_response (m);
 203 	} else
 204 		g_queue_push_tail (&user_message_queue, mail_msg_ref (m));
 205 }
 206 
 207 static void
 208 user_message_free (struct _user_message_msg *m)
 209 {
 210 	g_free (m->prompt);
 211 	g_slist_free_full (m->button_captions, g_free);
 212 	e_flag_free (m->done);
 213 }
 214 
 215 static MailMsgInfo user_message_info = {
 216 	sizeof (struct _user_message_msg),
 217 	(MailMsgDescFunc) NULL,
 218 	(MailMsgExecFunc) user_message_exec,
 219 	(MailMsgDoneFunc) NULL,
 220 	(MailMsgFreeFunc) user_message_free
 221 };
 222 
 223 /* Support for CamelSession.get_filter_driver () *****************************/
 224 
 225 static CamelFolder *
 226 get_folder (CamelFilterDriver *d,
 227             const gchar *uri,
 228             gpointer user_data,
 229             GError **error)
 230 {
 231 	EMailSession *session = E_MAIL_SESSION (user_data);
 232 
 233 	/* FIXME Not passing a GCancellable here. */
 234 	/* FIXME Need a camel_filter_driver_get_session(). */
 235 	return e_mail_session_uri_to_folder_sync (
 236 		session, uri, 0, NULL, error);
 237 }
 238 
 239 static CamelFilterDriver *
 240 main_get_filter_driver (CamelSession *session,
 241                         const gchar *type,
 242                         GError **error)
 243 {
 244 	CamelFilterDriver *driver;
 245 	EMailSession *ms = (EMailSession *) session;
 246 	GSettings *settings;
 247 
 248 	settings = g_settings_new ("org.gnome.evolution.mail");
 249 
 250 	driver = camel_filter_driver_new (session);
 251 	camel_filter_driver_set_folder_func (driver, get_folder, session);
 252 
 253 	if (g_settings_get_boolean (settings, "filters-log-actions")) {
 254 		if (ms->priv->filter_logfile == NULL) {
 255 			gchar *filename;
 256 
 257 			filename = g_settings_get_string (settings, "filters-log-file");
 258 			if (filename) {
 259 				ms->priv->filter_logfile = g_fopen (filename, "a+");
 260 				g_free (filename);
 261 			}
 262 		}
 263 
 264 		if (ms->priv->filter_logfile)
 265 			camel_filter_driver_set_logfile (driver, ms->priv->filter_logfile);
 266 	}
 267 
 268 	g_object_unref (settings);
 269 
 270 	return driver;
 271 }
 272 
 273 static gboolean
 274 session_forward_to_flush_outbox_cb (gpointer user_data)
 275 {
 276 	EMailSession *session = E_MAIL_SESSION (user_data);
 277 
 278 	g_mutex_lock (session->priv->preparing_flush_lock);
 279 	session->priv->preparing_flush = 0;
 280 	g_mutex_unlock (session->priv->preparing_flush_lock);
 281 
 282 	/* Connect to this and call mail_send in the main email client.*/
 283 	g_signal_emit (session, signals[FLUSH_OUTBOX], 0);
 284 
 285 	return FALSE;
 286 }
 287 
 288 static void
 289 async_context_free (AsyncContext *context)
 290 {
 291 	if (context->folder != NULL)
 292 		g_object_unref (context->folder);
 293 
 294 	g_free (context->uid);
 295 	g_free (context->uri);
 296 
 297 	g_slice_free (AsyncContext, context);
 298 }
 299 
 300 static gchar *
 301 mail_session_resolve_popb4smtp (ESourceRegistry *registry,
 302                                 CamelService *smtp_service)
 303 {
 304 	GList *list, *link;
 305 	const gchar *extension_name;
 306 	const gchar *smtp_uid;
 307 	gchar *pop_uid = NULL;
 308 
 309 	/* Find a POP account that uses the given smtp_service as its
 310 	 * transport.  XXX This isn't foolproof though, since we don't
 311 	 * check that the POP server is at the same domain as the SMTP
 312 	 * server, which is kind of the point of POPB4SMTP. */
 313 
 314 	smtp_uid = camel_service_get_uid (smtp_service);
 315 	g_return_val_if_fail (smtp_uid != NULL, NULL);
 316 
 317 	extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
 318 	list = e_source_registry_list_sources (registry, extension_name);
 319 
 320 	for (link = list; link != NULL; link = g_list_next (link)) {
 321 		ESource *source = E_SOURCE (link->data);
 322 		ESourceExtension *extension;
 323 		const gchar *backend_name;
 324 		gchar *uid;
 325 
 326 		extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
 327 		extension = e_source_get_extension (source, extension_name);
 328 
 329 		/* We're only interested in POP accounts. */
 330 
 331 		backend_name = e_source_backend_get_backend_name (
 332 			E_SOURCE_BACKEND (extension));
 333 		if (g_strcmp0 (backend_name, "pop") != 0)
 334 			continue;
 335 
 336 		/* Get the mail account's default mail identity. */
 337 
 338 		uid = e_source_mail_account_dup_identity_uid (
 339 			E_SOURCE_MAIL_ACCOUNT (extension));
 340 		source = e_source_registry_ref_source (registry, uid);
 341 		g_free (uid);
 342 
 343 		if (source == NULL)
 344 			continue;
 345 
 346 		/* Get the mail identity's default mail transport. */
 347 
 348 		extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
 349 		extension = e_source_get_extension (source, extension_name);
 350 
 351 		uid = e_source_mail_submission_dup_transport_uid (
 352 			E_SOURCE_MAIL_SUBMISSION (extension));
 353 
 354 		g_object_unref (source);
 355 
 356 		if (g_strcmp0 (uid, smtp_uid) == 0) {
 357 			pop_uid = uid;
 358 			break;
 359 		}
 360 
 361 		g_free (uid);
 362 	}
 363 
 364 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
 365 
 366 	return pop_uid;
 367 }
 368 
 369 static void
 370 mail_session_check_junk_notify (GSettings *settings,
 371                                 const gchar *key,
 372                                 CamelSession *session)
 373 {
 374 	if (strcmp (key, "junk-check-incoming") == 0)
 375 		camel_session_set_check_junk (
 376 			session, g_settings_get_boolean (settings, key));
 377 }
 378 
 379 static const gchar *
 380 mail_session_get_junk_filter_name (EMailSession *session)
 381 {
 382 	CamelJunkFilter *junk_filter;
 383 	GHashTableIter iter;
 384 	gpointer key, value;
 385 
 386 	/* XXX This property can be removed once Evolution moves to
 387 	 *     GSettings and can use transform functions when binding
 388 	 *     properties to settings.  That's why this is private. */
 389 
 390 	g_hash_table_iter_init (&iter, session->priv->junk_filters);
 391 	junk_filter = camel_session_get_junk_filter (CAMEL_SESSION (session));
 392 
 393 	while (g_hash_table_iter_next (&iter, &key, &value)) {
 394 		if (junk_filter == CAMEL_JUNK_FILTER (value))
 395 			return (const gchar *) key;
 396 	}
 397 
 398 	if (junk_filter != NULL)
 399 		g_warning (
 400 			"Camel is using a junk filter "
 401 			"unknown to Evolution of type %s",
 402 			G_OBJECT_TYPE_NAME (junk_filter));
 403 
 404 	return "";
 405 }
 406 
 407 static void
 408 mail_session_set_junk_filter_name (EMailSession *session,
 409                                    const gchar *junk_filter_name)
 410 {
 411 	CamelJunkFilter *junk_filter = NULL;
 412 
 413 	/* XXX This property can be removed once Evolution moves to
 414 	 *     GSettings and can use transform functions when binding
 415 	 *     properties to settings.  That's why this is private. */
 416 
 417 	/* An empty string is equivalent to a NULL string. */
 418 	if (junk_filter_name != NULL && *junk_filter_name == '\0')
 419 		junk_filter_name = NULL;
 420 
 421 	if (junk_filter_name != NULL) {
 422 		junk_filter = g_hash_table_lookup (
 423 			session->priv->junk_filters, junk_filter_name);
 424 		if (junk_filter != NULL) {
 425 			if (!e_mail_junk_filter_available (
 426 				E_MAIL_JUNK_FILTER (junk_filter)))
 427 				junk_filter = NULL;
 428 		} else {
 429 			g_warning (
 430 				"Unrecognized junk filter name "
 431 				"'%s' in GSettings", junk_filter_name);
 432 		}
 433 	}
 434 
 435 	camel_session_set_junk_filter (CAMEL_SESSION (session), junk_filter);
 436 
 437 	/* XXX We emit the "notify" signal in mail_session_notify(). */
 438 }
 439 
 440 static void
 441 mail_session_refresh_cb (ESource *source,
 442                          CamelSession *session)
 443 {
 444 	CamelService *service;
 445 	const gchar *uid;
 446 
 447 	uid = e_source_get_uid (source);
 448 	service = camel_session_ref_service (session, uid);
 449 	g_return_if_fail (service != NULL);
 450 
 451 	g_signal_emit (session, signals[REFRESH_SERVICE], 0, service);
 452 
 453 	g_object_unref (service);
 454 }
 455 
 456 static gboolean
 457 mail_session_check_goa_mail_disabled (EMailSession *session,
 458                                       ESource *source)
 459 {
 460 	ESource *goa_source;
 461 	ESourceRegistry *registry;
 462 	gboolean goa_mail_disabled = FALSE;
 463 
 464 	registry = e_mail_session_get_registry (session);
 465 
 466 	goa_source = e_source_registry_find_extension (
 467 		registry, source, E_SOURCE_EXTENSION_GOA);
 468 
 469 	if (goa_source != NULL) {
 470 		goa_mail_disabled = !e_source_get_enabled (source);
 471 		g_object_unref (goa_source);
 472 	}
 473 
 474 	return goa_mail_disabled;
 475 }
 476 
 477 static void
 478 mail_session_add_from_source (EMailSession *session,
 479                               CamelProviderType type,
 480                               ESource *source)
 481 {
 482 	ESourceBackend *extension;
 483 	CamelService *service;
 484 	const gchar *uid;
 485 	const gchar *backend_name;
 486 	const gchar *display_name;
 487 	const gchar *extension_name;
 488 	GError *error = NULL;
 489 
 490 	switch (type) {
 491 		case CAMEL_PROVIDER_STORE:
 492 			extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
 493 			break;
 494 		case CAMEL_PROVIDER_TRANSPORT:
 495 			extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
 496 			break;
 497 		default:
 498 			g_return_if_reached ();
 499 	}
 500 
 501 	uid = e_source_get_uid (source);
 502 	display_name = e_source_get_display_name (source);
 503 
 504 	extension = e_source_get_extension (source, extension_name);
 505 	backend_name = e_source_backend_get_backend_name (extension);
 506 
 507 	/* Sanity checks. */
 508 	g_return_if_fail (uid != NULL);
 509 	g_return_if_fail (backend_name != NULL);
 510 
 511 	/* Collection sources with a [GNOME Online Accounts] extension
 512 	 * require special handling.  If the collection's mail-enabled
 513 	 * flag is FALSE, do not add a CamelService.  The account must
 514 	 * not appear anywhere, not even in the Mail Accounts list. */
 515 	if (mail_session_check_goa_mail_disabled (session, source))
 516 		return;
 517 
 518 	service = camel_session_add_service (
 519 		CAMEL_SESSION (session), uid,
 520 		backend_name, type, &error);
 521 
 522 	/* Our own CamelSession.add_service() method will handle the
 523 	 * new CamelService, so we only need to unreference it here. */
 524 	if (service != NULL)
 525 		g_object_unref (service);
 526 
 527 	if (error != NULL) {
 528 		g_warning (
 529 			"Failed to add service '%s' (%s): %s",
 530 			display_name, uid, error->message);
 531 		g_error_free (error);
 532 	}
 533 
 534 	/* Set up auto-refresh. */
 535 	extension_name = E_SOURCE_EXTENSION_REFRESH;
 536 	if (e_source_has_extension (source, extension_name)) {
 537 		guint timeout_id;
 538 
 539 		/* Transports should not have a refresh extension. */
 540 		g_warn_if_fail (type != CAMEL_PROVIDER_TRANSPORT);
 541 
 542 		timeout_id = e_source_refresh_add_timeout (
 543 			source, NULL, (ESourceRefreshFunc)
 544 			mail_session_refresh_cb, session,
 545 			(GDestroyNotify) NULL);
 546 
 547 		g_hash_table_insert (
 548 			session->priv->auto_refresh_table,
 549 			g_strdup (uid),
 550 			GUINT_TO_POINTER (timeout_id));
 551 	}
 552 }
 553 
 554 static void
 555 mail_session_source_added_cb (ESourceRegistry *registry,
 556                               ESource *source,
 557                               EMailSession *session)
 558 {
 559 	CamelProviderType provider_type;
 560 	const gchar *extension_name;
 561 
 562 	provider_type = CAMEL_PROVIDER_STORE;
 563 	extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
 564 
 565 	if (e_source_has_extension (source, extension_name))
 566 		mail_session_add_from_source (session, provider_type, source);
 567 
 568 	provider_type = CAMEL_PROVIDER_TRANSPORT;
 569 	extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
 570 
 571 	if (e_source_has_extension (source, extension_name))
 572 		mail_session_add_from_source (session, provider_type, source);
 573 }
 574 
 575 static void
 576 mail_session_source_removed_cb (ESourceRegistry *registry,
 577                                 ESource *source,
 578                                 EMailSession *session)
 579 {
 580 	CamelSession *camel_session;
 581 	CamelService *service;
 582 	const gchar *uid;
 583 
 584 	camel_session = CAMEL_SESSION (session);
 585 
 586 	uid = e_source_get_uid (source);
 587 	service = camel_session_ref_service (camel_session, uid);
 588 
 589 	if (service != NULL) {
 590 		camel_session_remove_service (camel_session, service);
 591 		g_object_unref (service);
 592 	}
 593 }
 594 
 595 static void
 596 mail_session_source_enabled_cb (ESourceRegistry *registry,
 597                                 ESource *source,
 598                                 EMailSession *session)
 599 {
 600 	ESource *goa_source;
 601 
 602 	/* If the source is linked to a GNOME Online Account,
 603 	 * enabling the source is equivalent to adding it. */
 604 
 605 	goa_source = e_source_registry_find_extension (
 606 		registry, source, E_SOURCE_EXTENSION_GOA);
 607 
 608 	if (goa_source != NULL) {
 609 		mail_session_source_added_cb (registry, source, session);
 610 		g_object_unref (goa_source);
 611 	}
 612 }
 613 
 614 static void
 615 mail_session_source_disabled_cb (ESourceRegistry *registry,
 616                                  ESource *source,
 617                                  EMailSession *session)
 618 {
 619 	ESource *goa_source;
 620 
 621 	/* If the source is linked to a GNOME Online Account,
 622 	 * disabling the source is equivalent to removing it. */
 623 
 624 	goa_source = e_source_registry_find_extension (
 625 		registry, source, E_SOURCE_EXTENSION_GOA);
 626 
 627 	if (goa_source != NULL) {
 628 		mail_session_source_removed_cb (registry, source, session);
 629 		g_object_unref (goa_source);
 630 	}
 631 }
 632 
 633 static void
 634 mail_session_default_mail_account_cb (ESourceRegistry *registry,
 635                                       GParamSpec *pspec,
 636                                       EMailSession *session)
 637 {
 638 	ESource *source;
 639 	ESourceMailAccount *extension;
 640 	const gchar *extension_name;
 641 	gchar *uid;
 642 
 643 	/* If the default mail account names a valid mail
 644 	 * identity, make it the default mail identity. */
 645 
 646 	/* XXX I debated whether to have ESourceRegistry do this
 647 	 *     itself but it seems like an Evolution policy to me
 648 	 *     right now.  I may change my mind in the future, or
 649 	 *     decide not to do this synchronization at all. */
 650 
 651 	source = e_source_registry_ref_default_mail_account (registry);
 652 	g_return_if_fail (source != NULL);
 653 
 654 	extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
 655 	extension = e_source_get_extension (source, extension_name);
 656 	uid = e_source_mail_account_dup_identity_uid (extension);
 657 
 658 	g_object_unref (source);
 659 	source = NULL;
 660 
 661 	if (uid != NULL) {
 662 		source = e_source_registry_ref_source (registry, uid);
 663 		g_free (uid);
 664 	}
 665 
 666 	if (source != NULL) {
 667 		e_source_registry_set_default_mail_identity (registry, source);
 668 		g_object_unref (source);
 669 	}
 670 }
 671 
 672 static void
 673 mail_session_configure_local_store (EMailSession *session)
 674 {
 675 	CamelLocalSettings *local_settings;
 676 	CamelSession *camel_session;
 677 	CamelSettings *settings;
 678 	CamelService *service;
 679 	const gchar *data_dir;
 680 	const gchar *uid;
 681 	gchar *path;
 682 	gint ii;
 683 
 684 	camel_session = CAMEL_SESSION (session);
 685 
 686 	uid = E_MAIL_SESSION_LOCAL_UID;
 687 	service = camel_session_ref_service (camel_session, uid);
 688 	session->priv->local_store = service;  /* takes ownership */
 689 	g_return_if_fail (service != NULL);
 690 
 691 	settings = camel_service_ref_settings (service);
 692 
 693 	data_dir = camel_session_get_user_data_dir (camel_session);
 694 	path = g_build_filename (data_dir, E_MAIL_SESSION_LOCAL_UID, NULL);
 695 
 696 	local_settings = CAMEL_LOCAL_SETTINGS (settings);
 697 	camel_local_settings_set_path (local_settings, path);
 698 
 699 	g_free (path);
 700 
 701 	g_object_unref (settings);
 702 
 703 	/* Shouldn't need to worry about other mail applications
 704 	 * altering files in our local mail store. */
 705 	g_object_set (service, "need-summary-check", FALSE, NULL);
 706 
 707 	/* Populate the local folder cache. */
 708 	for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
 709 		CamelFolder *folder;
 710 		gchar *folder_uri;
 711 		const gchar *display_name;
 712 		GError *error = NULL;
 713 
 714 		display_name = local_folder_names[ii];
 715 
 716 		/* XXX This blocks but should be fast. */
 717 		if (ii == E_MAIL_LOCAL_FOLDER_LOCAL_INBOX)
 718 			folder = camel_store_get_inbox_folder_sync (
 719 				CAMEL_STORE (service), NULL, &error);
 720 		else
 721 			folder = camel_store_get_folder_sync (
 722 				CAMEL_STORE (service), display_name,
 723 				CAMEL_STORE_FOLDER_CREATE, NULL, &error);
 724 
 725 		folder_uri = e_mail_folder_uri_build (
 726 			CAMEL_STORE (service), display_name);
 727 
 728 		/* The arrays take ownership of the items added. */
 729 		g_ptr_array_add (session->priv->local_folders, folder);
 730 		g_ptr_array_add (session->priv->local_folder_uris, folder_uri);
 731 
 732 		if (error != NULL) {
 733 			g_critical ("%s: %s", G_STRFUNC, error->message);
 734 			g_error_free (error);
 735 		}
 736 	}
 737 }
 738 
 739 static void
 740 mail_session_configure_vfolder_store (EMailSession *session)
 741 {
 742 	CamelSession *camel_session;
 743 	CamelService *service;
 744 	const gchar *uid;
 745 
 746 	camel_session = CAMEL_SESSION (session);
 747 
 748 	uid = E_MAIL_SESSION_VFOLDER_UID;
 749 	service = camel_session_ref_service (camel_session, uid);
 750 	session->priv->vfolder_store = service;  /* takes ownership */
 751 	g_return_if_fail (service != NULL);
 752 
 753 	camel_service_connect_sync (service, NULL, NULL);
 754 
 755 	/* XXX There's more configuration to do in vfolder_load_storage()
 756 	 *     but it requires an EMailBackend, which we don't have access
 757 	 *     to from here, so it has to be called from elsewhere.  Kinda
 758 	 *     thinking about reworking that... */
 759 }
 760 
 761 static void
 762 mail_session_force_refresh (EMailSession *session)
 763 {
 764 	ESourceRegistry *registry;
 765 	GHashTableIter iter;
 766 	GSettings *settings;
 767 	gboolean unconditionally;
 768 	gpointer key;
 769 
 770 	/* Only refresh when the session is online. */
 771 	if (!camel_session_get_online (CAMEL_SESSION (session)))
 772 		return;
 773 
 774 	/* FIXME EMailSession should define properties for these. */
 775 	settings = g_settings_new ("org.gnome.evolution.mail");
 776 	unconditionally =
 777 		g_settings_get_boolean (settings, "send-recv-on-start") &&
 778 		g_settings_get_boolean (settings, "send-recv-all-on-start");
 779 	g_object_unref (settings);
 780 
 781 	registry = e_mail_session_get_registry (session);
 782 	g_hash_table_iter_init (&iter, session->priv->auto_refresh_table);
 783 
 784 	while (g_hash_table_iter_next (&iter, &key, NULL)) {
 785 		ESource *source;
 786 		ESourceRefresh *extension;
 787 		const gchar *extension_name;
 788 		gboolean refresh_enabled;
 789 
 790 		/* The hash table key is the ESource UID. */
 791 		source = e_source_registry_ref_source (registry, key);
 792 
 793 		if (source == NULL)
 794 			continue;
 795 
 796 		extension_name = E_SOURCE_EXTENSION_REFRESH;
 797 		extension = e_source_get_extension (source, extension_name);
 798 		refresh_enabled = e_source_refresh_get_enabled (extension);
 799 
 800 		if (refresh_enabled || unconditionally)
 801 			e_source_refresh_force_timeout (source);
 802 
 803 		g_object_unref (source);
 804 	}
 805 }
 806 
 807 static void
 808 mail_session_cancel_refresh (EMailSession *session)
 809 {
 810 	ESourceRegistry *registry;
 811 	GHashTableIter iter;
 812 	gpointer key, value;
 813 
 814 	registry = e_mail_session_get_registry (session);
 815 	g_hash_table_iter_init (&iter, session->priv->auto_refresh_table);
 816 
 817 	while (g_hash_table_iter_next (&iter, &key, &value)) {
 818 		ESource *source;
 819 		guint timeout_id;
 820 
 821 		/* The hash table key is the ESource UID. */
 822 		source = e_source_registry_ref_source (registry, key);
 823 
 824 		/* The hash table value is the refresh timeout ID. */
 825 		timeout_id = GPOINTER_TO_UINT (value);
 826 
 827 		if (source == NULL)
 828 			continue;
 829 
 830 		e_source_refresh_remove_timeout (source, timeout_id);
 831 
 832 		g_object_unref (source);
 833 	}
 834 
 835 	/* All timeouts cancelled so clear the auto-refresh table. */
 836 	g_hash_table_remove_all (session->priv->auto_refresh_table);
 837 }
 838 
 839 static gboolean
 840 mail_session_idle_refresh_cb (EMailSession *session)
 841 {
 842 	/* This only runs once at startup (if settings allow). */
 843 
 844 	if (camel_session_get_online (CAMEL_SESSION (session))) {
 845 		mail_session_force_refresh (session);
 846 
 847 		/* Also flush the Outbox. */
 848 		g_signal_emit (session, signals[FLUSH_OUTBOX], 0);
 849 	}
 850 
 851 	/* Listen for network state changes and force a
 852 	 * mail store refresh when coming back online. */
 853 	g_signal_connect (
 854 		session, "notify::online",
 855 		G_CALLBACK (mail_session_force_refresh), NULL);
 856 
 857 	return FALSE;
 858 }
 859 
 860 static void
 861 mail_session_set_registry (EMailSession *session,
 862                            ESourceRegistry *registry)
 863 {
 864 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
 865 	g_return_if_fail (session->priv->registry == NULL);
 866 
 867 	session->priv->registry = g_object_ref (registry);
 868 }
 869 
 870 static void
 871 mail_session_set_property (GObject *object,
 872                            guint property_id,
 873                            const GValue *value,
 874                            GParamSpec *pspec)
 875 {
 876 	switch (property_id) {
 877 		case PROP_JUNK_FILTER_NAME:
 878 			mail_session_set_junk_filter_name (
 879 				E_MAIL_SESSION (object),
 880 				g_value_get_string (value));
 881 			return;
 882 
 883 		case PROP_REGISTRY:
 884 			mail_session_set_registry (
 885 				E_MAIL_SESSION (object),
 886 				g_value_get_object (value));
 887 			return;
 888 	}
 889 
 890 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 891 }
 892 
 893 static void
 894 mail_session_get_property (GObject *object,
 895                            guint property_id,
 896                            GValue *value,
 897                            GParamSpec *pspec)
 898 {
 899 	switch (property_id) {
 900 		case PROP_FOLDER_CACHE:
 901 			g_value_set_object (
 902 				value,
 903 				e_mail_session_get_folder_cache (
 904 				E_MAIL_SESSION (object)));
 905 			return;
 906 
 907 		case PROP_JUNK_FILTER_NAME:
 908 			g_value_set_string (
 909 				value,
 910 				mail_session_get_junk_filter_name (
 911 				E_MAIL_SESSION (object)));
 912 			return;
 913 
 914 		case PROP_LOCAL_STORE:
 915 			g_value_set_object (
 916 				value,
 917 				e_mail_session_get_local_store (
 918 				E_MAIL_SESSION (object)));
 919 			return;
 920 
 921 		case PROP_REGISTRY:
 922 			g_value_set_object (
 923 				value,
 924 				e_mail_session_get_registry (
 925 				E_MAIL_SESSION (object)));
 926 			return;
 927 
 928 		case PROP_VFOLDER_STORE:
 929 			g_value_set_object (
 930 				value,
 931 				e_mail_session_get_vfolder_store (
 932 				E_MAIL_SESSION (object)));
 933 			return;
 934 	}
 935 
 936 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 937 }
 938 
 939 static void
 940 mail_session_dispose (GObject *object)
 941 {
 942 	EMailSessionPrivate *priv;
 943 
 944 	priv = E_MAIL_SESSION_GET_PRIVATE (object);
 945 
 946 	if (priv->folder_cache != NULL) {
 947 		g_object_unref (priv->folder_cache);
 948 		priv->folder_cache = NULL;
 949 	}
 950 
 951 	if (priv->registry != NULL) {
 952 		g_signal_handler_disconnect (
 953 			priv->registry,
 954 			priv->source_added_handler_id);
 955 		g_signal_handler_disconnect (
 956 			priv->registry,
 957 			priv->source_removed_handler_id);
 958 		g_signal_handler_disconnect (
 959 			priv->registry,
 960 			priv->source_enabled_handler_id);
 961 		g_signal_handler_disconnect (
 962 			priv->registry,
 963 			priv->source_disabled_handler_id);
 964 		g_signal_handler_disconnect (
 965 			priv->registry,
 966 			priv->default_mail_account_handler_id);
 967 
 968 		/* This requires the registry. */
 969 		mail_session_cancel_refresh (E_MAIL_SESSION (object));
 970 
 971 		g_object_unref (priv->registry);
 972 		priv->registry = NULL;
 973 	}
 974 
 975 	if (priv->local_store != NULL) {
 976 		g_object_unref (priv->local_store);
 977 		priv->local_store = NULL;
 978 	}
 979 
 980 	if (priv->vfolder_store != NULL) {
 981 		g_object_unref (priv->vfolder_store);
 982 		priv->vfolder_store = NULL;
 983 	}
 984 
 985 	g_ptr_array_set_size (priv->local_folders, 0);
 986 	g_ptr_array_set_size (priv->local_folder_uris, 0);
 987 
 988 	if (priv->preparing_flush > 0) {
 989 		g_source_remove (priv->preparing_flush);
 990 		priv->preparing_flush = 0;
 991 	}
 992 
 993 	/* Chain up to parent's dispose() method. */
 994 	G_OBJECT_CLASS (e_mail_session_parent_class)->dispose (object);
 995 }
 996 
 997 static void
 998 mail_session_finalize (GObject *object)
 999 {
1000 	EMailSessionPrivate *priv;
1001 
1002 	priv = E_MAIL_SESSION_GET_PRIVATE (object);
1003 
1004 	g_hash_table_destroy (priv->auto_refresh_table);
1005 	g_hash_table_destroy (priv->junk_filters);
1006 	g_object_unref (priv->proxy);
1007 
1008 	g_ptr_array_free (priv->local_folders, TRUE);
1009 	g_ptr_array_free (priv->local_folder_uris, TRUE);
1010 
1011 	g_mutex_free (priv->preparing_flush_lock);
1012 
1013 	g_free (mail_data_dir);
1014 	g_free (mail_config_dir);
1015 
1016 	/* Chain up to parent's finalize() method. */
1017 	G_OBJECT_CLASS (e_mail_session_parent_class)->finalize (object);
1018 }
1019 
1020 static void
1021 mail_session_notify (GObject *object,
1022                      GParamSpec *pspec)
1023 {
1024 	/* GObject does not implement this method; do not chain up. */
1025 
1026 	/* XXX Delete this once Evolution moves to GSettings and
1027 	 *     we're able to get rid of PROP_JUNK_FILTER_NAME. */
1028 	if (g_strcmp0 (pspec->name, "junk-filter") == 0)
1029 		g_object_notify (object, "junk-filter-name");
1030 }
1031 
1032 static void
1033 mail_session_constructed (GObject *object)
1034 {
1035 	EMailSession *session;
1036 	EExtensible *extensible;
1037 	ESourceRegistry *registry;
1038 	GType extension_type;
1039 	GList *list, *link;
1040 	GSettings *settings;
1041 	CamelProviderType provider_type;
1042 	const gchar *extension_name;
1043 	gulong handler_id;
1044 
1045 	session = E_MAIL_SESSION (object);
1046 	registry = e_mail_session_get_registry (session);
1047 
1048 	/* Chain up to parent's constructed() method. */
1049 	G_OBJECT_CLASS (e_mail_session_parent_class)->constructed (object);
1050 
1051 	/* Add available mail accounts. */
1052 
1053 	provider_type = CAMEL_PROVIDER_STORE;
1054 	extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
1055 
1056 	list = e_source_registry_list_sources (registry, extension_name);
1057 
1058 	for (link = list; link != NULL; link = g_list_next (link)) {
1059 		ESource *source = E_SOURCE (link->data);
1060 
1061 		mail_session_add_from_source (session, provider_type, source);
1062 	}
1063 
1064 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
1065 
1066 	/* Add available mail transports. */
1067 
1068 	provider_type = CAMEL_PROVIDER_TRANSPORT;
1069 	extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
1070 
1071 	list = e_source_registry_list_sources (registry, extension_name);
1072 
1073 	for (link = list; link != NULL; link = g_list_next (link)) {
1074 		ESource *source = E_SOURCE (link->data);
1075 
1076 		mail_session_add_from_source (session, provider_type, source);
1077 	}
1078 
1079 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
1080 
1081 	/* Built-in stores require extra configuration. */
1082 
1083 	mail_session_configure_local_store (session);
1084 	mail_session_configure_vfolder_store (session);
1085 
1086 	/* Listen for registry changes. */
1087 
1088 	handler_id = g_signal_connect (
1089 		registry, "source-added",
1090 		G_CALLBACK (mail_session_source_added_cb), session);
1091 	session->priv->source_added_handler_id = handler_id;
1092 
1093 	handler_id = g_signal_connect (
1094 		registry, "source-removed",
1095 		G_CALLBACK (mail_session_source_removed_cb), session);
1096 	session->priv->source_removed_handler_id = handler_id;
1097 
1098 	handler_id = g_signal_connect (
1099 		registry, "source-enabled",
1100 		G_CALLBACK (mail_session_source_enabled_cb), session);
1101 	session->priv->source_enabled_handler_id = handler_id;
1102 
1103 	handler_id = g_signal_connect (
1104 		registry, "source-disabled",
1105 		G_CALLBACK (mail_session_source_disabled_cb), session);
1106 	session->priv->source_disabled_handler_id = handler_id;
1107 
1108 	handler_id = g_signal_connect (
1109 		registry, "notify::default-mail-account",
1110 		G_CALLBACK (mail_session_default_mail_account_cb), session);
1111 	session->priv->default_mail_account_handler_id = handler_id;
1112 
1113 	extensible = E_EXTENSIBLE (object);
1114 	e_extensible_load_extensions (extensible);
1115 
1116 	/* Add junk filter extensions to an internal hash table. */
1117 
1118 	extension_type = E_TYPE_MAIL_JUNK_FILTER;
1119 	list = e_extensible_list_extensions (extensible, extension_type);
1120 
1121 	for (link = list; link != NULL; link = g_list_next (link)) {
1122 		EMailJunkFilter *junk_filter;
1123 		EMailJunkFilterClass *class;
1124 
1125 		junk_filter = E_MAIL_JUNK_FILTER (link->data);
1126 		class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
1127 
1128 		if (!CAMEL_IS_JUNK_FILTER (junk_filter)) {
1129 			g_warning (
1130 				"Skipping %s: Does not implement "
1131 				"CamelJunkFilterInterface",
1132 				G_OBJECT_TYPE_NAME (junk_filter));
1133 			continue;
1134 		}
1135 
1136 		if (class->filter_name == NULL) {
1137 			g_warning (
1138 				"Skipping %s: filter_name unset",
1139 				G_OBJECT_TYPE_NAME (junk_filter));
1140 			continue;
1141 		}
1142 
1143 		if (class->display_name == NULL) {
1144 			g_warning (
1145 				"Skipping %s: display_name unset",
1146 				G_OBJECT_TYPE_NAME (junk_filter));
1147 			continue;
1148 		}
1149 
1150 		/* No need to reference the EMailJunkFilter since
1151 		 * EMailSession owns the reference to it already. */
1152 		g_hash_table_insert (
1153 			session->priv->junk_filters,
1154 			(gpointer) class->filter_name,
1155 			junk_filter);
1156 	}
1157 
1158 	g_list_free (list);
1159 
1160 	settings = g_settings_new ("org.gnome.evolution.mail");
1161 
1162 	/* Bind the "junk-default-plugin" GSettings
1163 	 * key to our "junk-filter-name" property. */
1164 
1165 	g_settings_bind (
1166 		settings, "junk-default-plugin",
1167 		object, "junk-filter-name",
1168 		G_SETTINGS_BIND_DEFAULT);
1169 
1170 	camel_session_set_check_junk (
1171 		CAMEL_SESSION (session), g_settings_get_boolean (
1172 		settings, "junk-check-incoming"));
1173 	g_signal_connect (
1174 		settings, "changed",
1175 		G_CALLBACK (mail_session_check_junk_notify), session);
1176 
1177 	mail_config_reload_junk_headers (session);
1178 
1179 	e_proxy_setup_proxy (session->priv->proxy);
1180 
1181 	/* Initialize the legacy message-passing framework
1182 	 * before starting the first mail store refresh. */
1183 	mail_msg_init ();
1184 
1185 	/* The application is not yet fully initialized at this point,
1186 	 * so run the first mail store refresh from an idle callback. */
1187 	if (g_settings_get_boolean (settings, "send-recv-on-start"))
1188 		g_idle_add_full (
1189 			G_PRIORITY_DEFAULT,
1190 			(GSourceFunc) mail_session_idle_refresh_cb,
1191 			g_object_ref (session),
1192 			(GDestroyNotify) g_object_unref);
1193 
1194 	g_object_unref (settings);
1195 }
1196 
1197 static CamelService *
1198 mail_session_add_service (CamelSession *session,
1199                           const gchar *uid,
1200                           const gchar *protocol,
1201                           CamelProviderType type,
1202                           GError **error)
1203 {
1204 	ESourceRegistry *registry;
1205 	CamelService *service;
1206 	const gchar *extension_name;
1207 
1208 	registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1209 	extension_name = e_source_camel_get_extension_name (protocol);
1210 
1211 	/* Chain up to parents add_service() method. */
1212 	service = CAMEL_SESSION_CLASS (e_mail_session_parent_class)->
1213 		add_service (session, uid, protocol, type, error);
1214 
1215 	/* Configure the CamelService from the corresponding ESource. */
1216 
1217 	if (CAMEL_IS_SERVICE (service)) {
1218 		ESource *source;
1219 		ESource *tmp_source;
1220 
1221 		/* Each CamelService has a corresponding ESource. */
1222 		source = e_source_registry_ref_source (registry, uid);
1223 		g_return_val_if_fail (source != NULL, service);
1224 
1225 		tmp_source = e_source_registry_find_extension (
1226 			registry, source, extension_name);
1227 		if (tmp_source != NULL) {
1228 			g_object_unref (source);
1229 			source = tmp_source;
1230 		}
1231 
1232 		/* This handles all the messy property bindings. */
1233 		e_source_camel_configure_service (source, service);
1234 
1235 		g_object_bind_property (
1236 			source, "display-name",
1237 			service, "display-name",
1238 			G_BINDING_SYNC_CREATE);
1239 
1240 		/* Migrate files for this service from its old
1241 		 * URL-based directory to a UID-based directory
1242 		 * if necessary. */
1243 		camel_service_migrate_files (service);
1244 
1245 		g_object_unref (source);
1246 	}
1247 
1248 	return service;
1249 }
1250 
1251 static gchar *
1252 mail_session_get_password (CamelSession *session,
1253                            CamelService *service,
1254                            const gchar *prompt,
1255                            const gchar *item,
1256                            guint32 flags,
1257                            GError **error)
1258 {
1259 	ESourceRegistry *registry;
1260 	gchar *password = NULL;
1261 
1262 	/* XXX This method is now only for fringe cases.  For normal
1263 	 *     CamelService authentication, use authenticate_sync().
1264 	 *
1265 	 *     The two known fringe cases that still need this are:
1266 	 *
1267 	 *     1) CamelSaslPOPB4SMTP, where the CamelService is an SMTP
1268 	 *        transport and the item name is always "popb4smtp_uid".
1269 	 *        (This is a dirty hack, Camel just needs some way to
1270 	 *        pair up a CamelService and CamelTransport.  Not sure
1271 	 *        what that should look like just yet...)
1272 	 *
1273 	 *     2) CamelGpgContext, where the CamelService is NULL and
1274 	 *        the item name is a user ID (I think).  (Seahorse, or
1275 	 *        one of its dependent libraries, ought to handle this
1276 	 *        transparently once Camel fully transitions to GIO.)
1277 	 */
1278 
1279 	registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1280 
1281 	/* Handle the CamelSaslPOPB4SMTP case. */
1282 	if (g_strcmp0 (item, "popb4smtp_uid") == 0)
1283 		return mail_session_resolve_popb4smtp (registry, service);
1284 
1285 	/* Otherwise this had better be the CamelGpgContext case. */
1286 	g_return_val_if_fail (service == NULL, NULL);
1287 
1288 	password = e_passwords_get_password (NULL, item);
1289 
1290 	if (password == NULL || (flags & CAMEL_SESSION_PASSWORD_REPROMPT)) {
1291 		gboolean remember;
1292 		guint eflags = 0;
1293 
1294 		if (flags & CAMEL_SESSION_PASSWORD_STATIC)
1295 			eflags |= E_PASSWORDS_REMEMBER_NEVER;
1296 		else
1297 			eflags |= E_PASSWORDS_REMEMBER_SESSION;
1298 
1299 		if (flags & CAMEL_SESSION_PASSWORD_REPROMPT)
1300 			eflags |= E_PASSWORDS_REPROMPT;
1301 
1302 		if (flags & CAMEL_SESSION_PASSWORD_SECRET)
1303 			eflags |= E_PASSWORDS_SECRET;
1304 
1305 		if (flags & CAMEL_SESSION_PASSPHRASE)
1306 			eflags |= E_PASSWORDS_PASSPHRASE;
1307 
1308 		password = e_passwords_ask_password (
1309 			"", NULL, item, prompt, eflags, &remember, NULL);
1310 
1311 		if (password == NULL)
1312 			e_passwords_forget_password (NULL, item);
1313 	}
1314 
1315 	if (password == NULL)
1316 		g_set_error (
1317 			error, G_IO_ERROR,
1318 			G_IO_ERROR_CANCELLED,
1319 			_("User cancelled operation"));
1320 
1321 	return password;
1322 }
1323 
1324 static gboolean
1325 mail_session_forget_password (CamelSession *session,
1326                               CamelService *service,
1327                               const gchar *item,
1328                               GError **error)
1329 {
1330 	/* XXX The only remaining user of this method is CamelGpgContext,
1331 	 *     which does not provide a CamelService.  Use 'item' as the
1332 	 *     password key. */
1333 
1334 	g_return_val_if_fail (service == NULL, FALSE);
1335 
1336 	e_passwords_forget_password (NULL, item);
1337 
1338 	return TRUE;
1339 }
1340 
1341 static gint
1342 mail_session_alert_user (CamelSession *session,
1343                          CamelSessionAlertType type,
1344                          const gchar *prompt,
1345                          GSList *button_captions)
1346 {
1347 	struct _user_message_msg *m;
1348 	GCancellable *cancellable;
1349 	gint result = -1;
1350 	GSList *iter;
1351 
1352 	m = mail_msg_new (&user_message_info);
1353 	m->ismain = mail_in_main_thread ();
1354 	m->type = type;
1355 	m->prompt = g_strdup (prompt);
1356 	m->done = e_flag_new ();
1357 	m->button_captions = g_slist_copy (button_captions);
1358 
1359 	for (iter = m->button_captions; iter; iter = iter->next)
1360 		iter->data = g_strdup (iter->data);
1361 
1362 	if (g_slist_length (button_captions) > 1)
1363 		mail_msg_ref (m);
1364 
1365 	cancellable = m->base.cancellable;
1366 
1367 	if (m->ismain)
1368 		user_message_exec (m, cancellable, &m->base.error);
1369 	else
1370 		mail_msg_main_loop_push (m);
1371 
1372 	if (g_slist_length (button_captions) > 1) {
1373 		e_flag_wait (m->done);
1374 		result = m->result;
1375 		mail_msg_unref (m);
1376 	} else if (m->ismain)
1377 		mail_msg_unref (m);
1378 
1379 	return result;
1380 }
1381 
1382 static CamelFilterDriver *
1383 mail_session_get_filter_driver (CamelSession *session,
1384                                 const gchar *type,
1385                                 GError **error)
1386 {
1387 	return (CamelFilterDriver *) mail_call_main (
1388 		MAIL_CALL_p_ppp, (MailMainFunc) main_get_filter_driver,
1389 		session, type, error);
1390 }
1391 
1392 static gboolean
1393 mail_session_lookup_addressbook (CamelSession *session,
1394                                  const gchar *name)
1395 {
1396 	ESourceRegistry *registry;
1397 	CamelInternetAddress *addr;
1398 	gboolean ret;
1399 
1400 	if (!mail_config_get_lookup_book ())
1401 		return FALSE;
1402 
1403 	registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1404 
1405 	addr = camel_internet_address_new ();
1406 	camel_address_decode ((CamelAddress *) addr, name);
1407 	ret = em_utils_in_addressbook (
1408 		registry, addr, mail_config_get_lookup_book_local_only (), NULL);
1409 	g_object_unref (addr);
1410 
1411 	return ret;
1412 }
1413 
1414 static void
1415 mail_session_get_socks_proxy (CamelSession *session,
1416                               const gchar *for_host,
1417                               gchar **host_ret,
1418                               gint *port_ret)
1419 {
1420 	EMailSession *mail_session;
1421 	gchar *uri;
1422 
1423 	g_return_if_fail (session != NULL);
1424 	g_return_if_fail (for_host != NULL);
1425 	g_return_if_fail (host_ret != NULL);
1426 	g_return_if_fail (port_ret != NULL);
1427 
1428 	mail_session = E_MAIL_SESSION (session);
1429 	g_return_if_fail (mail_session != NULL);
1430 	g_return_if_fail (mail_session->priv != NULL);
1431 
1432 	*host_ret = NULL;
1433 	*port_ret = 0;
1434 
1435 	uri = g_strconcat ("socks://", for_host, NULL);
1436 
1437 	if (e_proxy_require_proxy_for_uri (mail_session->priv->proxy, uri)) {
1438 		SoupURI *suri;
1439 
1440 		suri = e_proxy_peek_uri_for (mail_session->priv->proxy, uri);
1441 		if (suri) {
1442 			*host_ret = g_strdup (suri->host);
1443 			*port_ret = suri->port;
1444 		}
1445 	}
1446 
1447 	g_free (uri);
1448 }
1449 
1450 static gboolean
1451 mail_session_authenticate_sync (CamelSession *session,
1452                                 CamelService *service,
1453                                 const gchar *mechanism,
1454                                 GCancellable *cancellable,
1455                                 GError **error)
1456 {
1457 	ESource *source;
1458 	ESourceRegistry *registry;
1459 	ESourceAuthenticator *auth;
1460 	CamelServiceAuthType *authtype = NULL;
1461 	CamelAuthenticationResult result;
1462 	const gchar *uid;
1463 	gboolean authenticated;
1464 	GError *local_error = NULL;
1465 
1466 	/* Do not chain up.  Camel's default method is only an example for
1467 	 * subclasses to follow.  Instead we mimic most of its logic here. */
1468 
1469 	registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1470 
1471 	/* Treat a mechanism name of "none" as NULL. */
1472 	if (g_strcmp0 (mechanism, "none") == 0)
1473 		mechanism = NULL;
1474 
1475 	/* APOP is one case where a non-SASL mechanism name is passed, so
1476 	 * don't bail if the CamelServiceAuthType struct comes back NULL. */
1477 	if (mechanism != NULL)
1478 		authtype = camel_sasl_authtype (mechanism);
1479 
1480 	/* If the SASL mechanism does not involve a user
1481 	 * password, then it gets one shot to authenticate. */
1482 	if (authtype != NULL && !authtype->need_password) {
1483 		result = camel_service_authenticate_sync (
1484 			service, mechanism, cancellable, error);
1485 		if (result == CAMEL_AUTHENTICATION_REJECTED)
1486 			g_set_error (
1487 				error, CAMEL_SERVICE_ERROR,
1488 				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1489 				_("%s authentication failed"), mechanism);
1490 		return (result == CAMEL_AUTHENTICATION_ACCEPTED);
1491 	}
1492 
1493 	/* Some SASL mechanisms can attempt to authenticate without a
1494 	 * user password being provided (e.g. single-sign-on credentials),
1495 	 * but can fall back to a user password.  Handle that case next. */
1496 	if (mechanism != NULL) {
1497 		CamelProvider *provider;
1498 		CamelSasl *sasl;
1499 		const gchar *service_name;
1500 		gboolean success = FALSE;
1501 
1502 		provider = camel_service_get_provider (service);
1503 		service_name = provider->protocol;
1504 
1505 		/* XXX Would be nice if camel_sasl_try_empty_password_sync()
1506 		 *     returned CamelAuthenticationResult so it's easier to
1507 		 *     detect errors. */
1508 		sasl = camel_sasl_new (service_name, mechanism, service);
1509 		if (sasl != NULL) {
1510 			success = camel_sasl_try_empty_password_sync (
1511 				sasl, cancellable, &local_error);
1512 			g_object_unref (sasl);
1513 		}
1514 
1515 		if (success)
1516 			return TRUE;
1517 	}
1518 
1519 	/* Abort authentication if we got cancelled.
1520 	 * Otherwise clear any errors and press on. */
1521 	if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1522 		return FALSE;
1523 
1524 	g_clear_error (&local_error);
1525 
1526 	/* Find a matching ESource for this CamelService. */
1527 	uid = camel_service_get_uid (service);
1528 	source = e_source_registry_ref_source (registry, uid);
1529 
1530 	if (source == NULL) {
1531 		g_set_error (
1532 			error, CAMEL_SERVICE_ERROR,
1533 			CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1534 			_("No data source found for UID '%s'"), uid);
1535 		return FALSE;
1536 	}
1537 
1538 	auth = e_mail_authenticator_new (service, mechanism);
1539 
1540 	authenticated = e_source_registry_authenticate_sync (
1541 		registry, source, auth, cancellable, error);
1542 
1543 	g_object_unref (auth);
1544 
1545 	g_object_unref (source);
1546 
1547 	return authenticated;
1548 }
1549 
1550 static gboolean
1551 mail_session_forward_to_sync (CamelSession *session,
1552                               CamelFolder *folder,
1553                               CamelMimeMessage *message,
1554                               const gchar *address,
1555                               GCancellable *cancellable,
1556                               GError **error)
1557 {
1558 	EMailSessionPrivate *priv;
1559 	ESource *source;
1560 	ESourceRegistry *registry;
1561 	ESourceMailIdentity *extension;
1562 	CamelMimeMessage *forward;
1563 	CamelStream *mem;
1564 	CamelInternetAddress *addr;
1565 	CamelFolder *out_folder;
1566 	CamelMessageInfo *info;
1567 	CamelMedium *medium;
1568 	const gchar *extension_name;
1569 	const gchar *from_address;
1570 	const gchar *from_name;
1571 	const gchar *header_name;
1572 	struct _camel_header_raw *xev;
1573 	gboolean success;
1574 	gchar *subject;
1575 
1576 	g_return_val_if_fail (folder != NULL, FALSE);
1577 	g_return_val_if_fail (message != NULL, FALSE);
1578 	g_return_val_if_fail (address != NULL, FALSE);
1579 
1580 	priv = E_MAIL_SESSION_GET_PRIVATE (session);
1581 
1582 	if (!*address) {
1583 		g_set_error (
1584 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1585 			_("No destination address provided, forwarding "
1586 			"of the message has been cancelled."));
1587 		return FALSE;
1588 	}
1589 
1590 	registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1591 
1592 	/* This returns a new ESource reference. */
1593 	source = em_utils_guess_mail_identity_with_recipients (
1594 		registry, message, folder, NULL);
1595 	if (source == NULL) {
1596 		g_set_error (
1597 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1598 			_("No identity found to use, forwarding "
1599 			"of the message has been cancelled."));
1600 		return FALSE;
1601 	}
1602 
1603 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1604 	extension = e_source_get_extension (source, extension_name);
1605 	from_address = e_source_mail_identity_get_address (extension);
1606 	from_name = e_source_mail_identity_get_name (extension);
1607 
1608 	forward = camel_mime_message_new ();
1609 
1610 	/* make copy of the message, because we are going to modify it */
1611 	mem = camel_stream_mem_new ();
1612 	camel_data_wrapper_write_to_stream_sync (
1613 		CAMEL_DATA_WRAPPER (message), mem, NULL, NULL);
1614 	g_seekable_seek (G_SEEKABLE (mem), 0, G_SEEK_SET, NULL, NULL);
1615 	camel_data_wrapper_construct_from_stream_sync (
1616 		CAMEL_DATA_WRAPPER (forward), mem, NULL, NULL);
1617 	g_object_unref (mem);
1618 
1619 	/* clear previous recipients */
1620 	camel_mime_message_set_recipients (
1621 		forward, CAMEL_RECIPIENT_TYPE_TO, NULL);
1622 	camel_mime_message_set_recipients (
1623 		forward, CAMEL_RECIPIENT_TYPE_CC, NULL);
1624 	camel_mime_message_set_recipients (
1625 		forward, CAMEL_RECIPIENT_TYPE_BCC, NULL);
1626 	camel_mime_message_set_recipients (
1627 		forward, CAMEL_RECIPIENT_TYPE_RESENT_TO, NULL);
1628 	camel_mime_message_set_recipients (
1629 		forward, CAMEL_RECIPIENT_TYPE_RESENT_CC, NULL);
1630 	camel_mime_message_set_recipients (
1631 		forward, CAMEL_RECIPIENT_TYPE_RESENT_BCC, NULL);
1632 
1633 	medium = CAMEL_MEDIUM (forward);
1634 
1635 	/* remove all delivery and notification headers */
1636 	header_name = "Disposition-Notification-To";
1637 	while (camel_medium_get_header (medium, header_name))
1638 		camel_medium_remove_header (medium, header_name);
1639 
1640 	header_name = "Delivered-To";
1641 	while (camel_medium_get_header (medium, header_name))
1642 		camel_medium_remove_header (medium, header_name);
1643 
1644 	/* remove any X-Evolution-* headers that may have been set */
1645 	xev = mail_tool_remove_xevolution_headers (forward);
1646 	camel_header_raw_clear (&xev);
1647 
1648 	/* from */
1649 	addr = camel_internet_address_new ();
1650 	camel_internet_address_add (addr, from_name, from_address);
1651 	camel_mime_message_set_from (forward, addr);
1652 	g_object_unref (addr);
1653 
1654 	/* to */
1655 	addr = camel_internet_address_new ();
1656 	camel_address_decode (CAMEL_ADDRESS (addr), address);
1657 	camel_mime_message_set_recipients (
1658 		forward, CAMEL_RECIPIENT_TYPE_TO, addr);
1659 	g_object_unref (addr);
1660 
1661 	/* subject */
1662 	subject = mail_tool_generate_forward_subject (message);
1663 	camel_mime_message_set_subject (forward, subject);
1664 	g_free (subject);
1665 
1666 	/* and send it */
1667 	info = camel_message_info_new (NULL);
1668 	out_folder = e_mail_session_get_local_folder (
1669 		E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_OUTBOX);
1670 	camel_message_info_set_flags (
1671 		info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
1672 
1673 	success = e_mail_folder_append_message_sync (
1674 		out_folder, forward, info, NULL, cancellable, error);
1675 
1676 	if (success) {
1677 		GSettings *settings;
1678 		gboolean flush_outbox;
1679 
1680 		settings = g_settings_new ("org.gnome.evolution.mail");
1681 		flush_outbox = g_settings_get_boolean (settings, "flush-outbox");
1682 		g_object_unref (settings);
1683 
1684 		g_mutex_lock (priv->preparing_flush_lock);
1685 
1686 		if (priv->preparing_flush > 0) {
1687 			g_source_remove (priv->preparing_flush);
1688 			flush_outbox = TRUE;
1689 		}
1690 
1691 		if (flush_outbox) {
1692 			GMainContext *main_context;
1693 			GSource *timeout_source;
1694 
1695 			main_context =
1696 				camel_session_get_main_context (session);
1697 
1698 			timeout_source =
1699 				g_timeout_source_new_seconds (60);
1700 			g_source_set_callback (
1701 				timeout_source,
1702 				session_forward_to_flush_outbox_cb,
1703 				session, (GDestroyNotify) NULL);
1704 			priv->preparing_flush = g_source_attach (
1705 				timeout_source, main_context);
1706 			g_source_unref (timeout_source);
1707 		}
1708 
1709 		g_mutex_unlock (priv->preparing_flush_lock);
1710 	}
1711 
1712 	camel_message_info_free (info);
1713 
1714 	g_object_unref (source);
1715 
1716 	return success;
1717 }
1718 
1719 static EMVFolderContext *
1720 mail_session_create_vfolder_context (EMailSession *session)
1721 {
1722 	return em_vfolder_context_new ();
1723 }
1724 
1725 static void
1726 e_mail_session_class_init (EMailSessionClass *class)
1727 {
1728 	GObjectClass *object_class;
1729 	CamelSessionClass *session_class;
1730 
1731 	g_type_class_add_private (class, sizeof (EMailSessionPrivate));
1732 
1733 	object_class = G_OBJECT_CLASS (class);
1734 	object_class->set_property = mail_session_set_property;
1735 	object_class->get_property = mail_session_get_property;
1736 	object_class->dispose = mail_session_dispose;
1737 	object_class->finalize = mail_session_finalize;
1738 	object_class->notify = mail_session_notify;
1739 	object_class->constructed = mail_session_constructed;
1740 
1741 	session_class = CAMEL_SESSION_CLASS (class);
1742 	session_class->add_service = mail_session_add_service;
1743 	session_class->get_password = mail_session_get_password;
1744 	session_class->forget_password = mail_session_forget_password;
1745 	session_class->alert_user = mail_session_alert_user;
1746 	session_class->get_filter_driver = mail_session_get_filter_driver;
1747 	session_class->lookup_addressbook = mail_session_lookup_addressbook;
1748 	session_class->get_socks_proxy = mail_session_get_socks_proxy;
1749 	session_class->authenticate_sync = mail_session_authenticate_sync;
1750 	session_class->forward_to_sync = mail_session_forward_to_sync;
1751 
1752 	class->create_vfolder_context = mail_session_create_vfolder_context;
1753 
1754 	g_object_class_install_property (
1755 		object_class,
1756 		PROP_FOLDER_CACHE,
1757 		g_param_spec_object (
1758 			"folder-cache",
1759 			NULL,
1760 			NULL,
1761 			MAIL_TYPE_FOLDER_CACHE,
1762 			G_PARAM_READABLE |
1763 			G_PARAM_STATIC_STRINGS));
1764 
1765 	/* XXX This property can be removed once Evolution moves to
1766 	 *     GSettings and can use transform functions when binding
1767 	 *     properties to settings. */
1768 	g_object_class_install_property (
1769 		object_class,
1770 		PROP_JUNK_FILTER_NAME,
1771 		g_param_spec_string (
1772 			"junk-filter-name",
1773 			NULL,
1774 			NULL,
1775 			NULL,
1776 			G_PARAM_READWRITE |
1777 			G_PARAM_STATIC_STRINGS));
1778 
1779 	g_object_class_install_property (
1780 		object_class,
1781 		PROP_LOCAL_STORE,
1782 		g_param_spec_object (
1783 			"local-store",
1784 			"Local Store",
1785 			"Built-in local store",
1786 			CAMEL_TYPE_STORE,
1787 			G_PARAM_READABLE |
1788 			G_PARAM_STATIC_STRINGS));
1789 
1790 	g_object_class_install_property (
1791 		object_class,
1792 		PROP_REGISTRY,
1793 		g_param_spec_object (
1794 			"registry",
1795 			"Registry",
1796 			"Data source registry",
1797 			E_TYPE_SOURCE_REGISTRY,
1798 			G_PARAM_READWRITE |
1799 			G_PARAM_CONSTRUCT_ONLY |
1800 			G_PARAM_STATIC_STRINGS));
1801 
1802 	g_object_class_install_property (
1803 		object_class,
1804 		PROP_VFOLDER_STORE,
1805 		g_param_spec_object (
1806 			"vfolder-store",
1807 			"Search Folder Store",
1808 			"Built-in search folder store",
1809 			CAMEL_TYPE_STORE,
1810 			G_PARAM_READABLE |
1811 			G_PARAM_STATIC_STRINGS));
1812 
1813 	/**
1814 	 * EMailSession::flush-outbox
1815 	 * @session: the email session
1816 	 *
1817 	 * Emitted if the send folder should be flushed.
1818 	 **/
1819 	signals[FLUSH_OUTBOX] = g_signal_new (
1820 		"flush-outbox",
1821 		G_OBJECT_CLASS_TYPE (object_class),
1822 		G_SIGNAL_RUN_FIRST,
1823 		G_STRUCT_OFFSET (EMailSessionClass, flush_outbox),
1824 		NULL, NULL,
1825 		g_cclosure_marshal_VOID__VOID,
1826 		G_TYPE_NONE, 0);
1827 
1828 	/**
1829 	 * EMailSession::refresh-service
1830 	 * @session: the #EMailSession that emitted the signal
1831 	 * @service: a #CamelService
1832 	 *
1833 	 * Emitted when @service should be refreshed.
1834 	 **/
1835 	signals[REFRESH_SERVICE] = g_signal_new (
1836 		"refresh-service",
1837 		G_OBJECT_CLASS_TYPE (object_class),
1838 		G_SIGNAL_RUN_LAST,
1839 		G_STRUCT_OFFSET (EMailSessionClass, refresh_service),
1840 		NULL, NULL,
1841 		g_cclosure_marshal_VOID__OBJECT,
1842 		G_TYPE_NONE, 1,
1843 		CAMEL_TYPE_SERVICE);
1844 
1845 	/**
1846 	 * EMailSession::store-added
1847 	 * @session: the #EMailSession that emitted the signal
1848 	 * @store: a #CamelStore
1849 	 *
1850 	 * Emitted when a store is added
1851 	 **/
1852 	signals[STORE_ADDED] = g_signal_new (
1853 		"store-added",
1854 		G_OBJECT_CLASS_TYPE (object_class),
1855 		G_SIGNAL_RUN_FIRST,
1856 		G_STRUCT_OFFSET (EMailSessionClass, store_added),
1857 		NULL, NULL,
1858 		g_cclosure_marshal_VOID__OBJECT,
1859 		G_TYPE_NONE, 1,
1860 		CAMEL_TYPE_STORE);
1861 
1862 	/**
1863 	 * EMailSession::store-removed
1864 	 * @session: the #EMailSession that emitted the signal
1865 	 * @store: a #CamelStore
1866 	 *
1867 	 * Emitted when a store is removed 
1868 	 **/
1869 	signals[STORE_REMOVED] = g_signal_new (
1870 		"store-removed",
1871 		G_OBJECT_CLASS_TYPE (object_class),
1872 		G_SIGNAL_RUN_FIRST,
1873 		G_STRUCT_OFFSET (EMailSessionClass, store_removed),
1874 		NULL, NULL,
1875 		g_cclosure_marshal_VOID__OBJECT,
1876 		G_TYPE_NONE, 1,
1877 		CAMEL_TYPE_STORE);
1878 
1879 	camel_null_store_register_provider ();
1880 
1881 	/* Make sure ESourceCamel picks up the "none" provider. */
1882 	e_source_camel_generate_subtype ("none", CAMEL_TYPE_SETTINGS);
1883 }
1884 
1885 static void
1886 e_mail_session_init (EMailSession *session)
1887 {
1888 	GHashTable *auto_refresh_table;
1889 	GHashTable *junk_filters;
1890 
1891 	auto_refresh_table = g_hash_table_new_full (
1892 		(GHashFunc) g_str_hash,
1893 		(GEqualFunc) g_str_equal,
1894 		(GDestroyNotify) g_free,
1895 		(GDestroyNotify) NULL);
1896 
1897 	junk_filters = g_hash_table_new (
1898 		(GHashFunc) g_str_hash,
1899 		(GEqualFunc) g_str_equal);
1900 
1901 	session->priv = E_MAIL_SESSION_GET_PRIVATE (session);
1902 	session->priv->folder_cache = mail_folder_cache_new (session);
1903 	session->priv->auto_refresh_table = auto_refresh_table;
1904 	session->priv->junk_filters = junk_filters;
1905 	session->priv->proxy = e_proxy_new ();
1906 
1907 	session->priv->local_folders =
1908 		g_ptr_array_new_with_free_func (
1909 		(GDestroyNotify) g_object_unref);
1910 	session->priv->local_folder_uris =
1911 		g_ptr_array_new_with_free_func (
1912 		(GDestroyNotify) g_free);
1913 
1914 	session->priv->preparing_flush_lock = g_mutex_new ();
1915 }
1916 
1917 EMailSession *
1918 e_mail_session_new (ESourceRegistry *registry)
1919 {
1920 	const gchar *user_data_dir;
1921 	const gchar *user_cache_dir;
1922 
1923 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
1924 
1925 	user_data_dir = mail_session_get_data_dir ();
1926 	user_cache_dir = mail_session_get_cache_dir ();
1927 
1928 	return g_object_new (
1929 		E_TYPE_MAIL_SESSION,
1930 		"user-data-dir", user_data_dir,
1931 		"user-cache-dir", user_cache_dir,
1932 		"registry", registry,
1933 		NULL);
1934 }
1935 
1936 ESourceRegistry *
1937 e_mail_session_get_registry (EMailSession *session)
1938 {
1939 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1940 
1941 	return session->priv->registry;
1942 }
1943 
1944 MailFolderCache *
1945 e_mail_session_get_folder_cache (EMailSession *session)
1946 {
1947 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1948 
1949 	return session->priv->folder_cache;
1950 }
1951 
1952 CamelStore *
1953 e_mail_session_get_local_store (EMailSession *session)
1954 {
1955 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1956 
1957 	return CAMEL_STORE (session->priv->local_store);
1958 }
1959 
1960 CamelFolder *
1961 e_mail_session_get_local_folder (EMailSession *session,
1962                                  EMailLocalFolder type)
1963 {
1964 	GPtrArray *local_folders;
1965 	CamelFolder *folder;
1966 
1967 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1968 
1969 	local_folders = session->priv->local_folders;
1970 	g_return_val_if_fail (type < local_folders->len, NULL);
1971 
1972 	folder = g_ptr_array_index (local_folders, type);
1973 	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1974 
1975 	return folder;
1976 }
1977 
1978 const gchar *
1979 e_mail_session_get_local_folder_uri (EMailSession *session,
1980                                      EMailLocalFolder type)
1981 {
1982 	GPtrArray *local_folder_uris;
1983 	const gchar *folder_uri;
1984 
1985 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1986 
1987 	local_folder_uris = session->priv->local_folder_uris;
1988 	g_return_val_if_fail (type < local_folder_uris->len, NULL);
1989 
1990 	folder_uri = g_ptr_array_index (local_folder_uris, type);
1991 	g_return_val_if_fail (folder_uri != NULL, NULL);
1992 
1993 	return folder_uri;
1994 }
1995 
1996 GList *
1997 e_mail_session_get_available_junk_filters (EMailSession *session)
1998 {
1999 	GList *list, *link;
2000 	GQueue trash = G_QUEUE_INIT;
2001 
2002 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2003 
2004 	list = g_hash_table_get_values (session->priv->junk_filters);
2005 
2006 	/* Discard unavailable junk filters.  (e.g. Junk filter
2007 	 * requires Bogofilter but Bogofilter is not installed,
2008 	 * hence the junk filter is unavailable.) */
2009 
2010 	for (link = list; link != NULL; link = g_list_next (link)) {
2011 		EMailJunkFilter *junk_filter;
2012 
2013 		junk_filter = E_MAIL_JUNK_FILTER (link->data);
2014 		if (!e_mail_junk_filter_available (junk_filter))
2015 			g_queue_push_tail (&trash, link);
2016 	}
2017 
2018 	while ((link = g_queue_pop_head (&trash)) != NULL)
2019 		list = g_list_delete_link (list, link);
2020 
2021 	/* Sort the remaining junk filters by display name. */
2022 
2023 	return g_list_sort (list, (GCompareFunc) e_mail_junk_filter_compare);
2024 }
2025 
2026 static void
2027 mail_session_get_inbox_thread (GSimpleAsyncResult *simple,
2028                                EMailSession *session,
2029                                GCancellable *cancellable)
2030 {
2031 	AsyncContext *context;
2032 	GError *error = NULL;
2033 
2034 	context = g_simple_async_result_get_op_res_gpointer (simple);
2035 
2036 	context->folder = e_mail_session_get_inbox_sync (
2037 		session, context->uid, cancellable, &error);
2038 
2039 	if (error != NULL)
2040 		g_simple_async_result_take_error (simple, error);
2041 }
2042 
2043 CamelFolder *
2044 e_mail_session_get_inbox_sync (EMailSession *session,
2045                                const gchar *service_uid,
2046                                GCancellable *cancellable,
2047                                GError **error)
2048 {
2049 	CamelService *service;
2050 	CamelFolder *folder = NULL;
2051 
2052 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2053 	g_return_val_if_fail (service_uid != NULL, NULL);
2054 
2055 	service = camel_session_ref_service (
2056 		CAMEL_SESSION (session), service_uid);
2057 
2058 	if (service == NULL)
2059 		return NULL;
2060 
2061 	if (!CAMEL_IS_STORE (service))
2062 		goto exit;
2063 
2064 	if (!camel_service_connect_sync (service, cancellable, error))
2065 		goto exit;
2066 
2067 	folder = camel_store_get_inbox_folder_sync (
2068 		CAMEL_STORE (service), cancellable, error);
2069 
2070 exit:
2071 	g_object_unref (service);
2072 
2073 	return folder;
2074 }
2075 
2076 void
2077 e_mail_session_get_inbox (EMailSession *session,
2078                           const gchar *service_uid,
2079                           gint io_priority,
2080                           GCancellable *cancellable,
2081                           GAsyncReadyCallback callback,
2082                           gpointer user_data)
2083 {
2084 	GSimpleAsyncResult *simple;
2085 	AsyncContext *context;
2086 
2087 	g_return_if_fail (E_IS_MAIL_SESSION (session));
2088 	g_return_if_fail (service_uid != NULL);
2089 
2090 	context = g_slice_new0 (AsyncContext);
2091 	context->uid = g_strdup (service_uid);
2092 
2093 	simple = g_simple_async_result_new (
2094 		G_OBJECT (session), callback,
2095 		user_data, e_mail_session_get_inbox);
2096 
2097 	g_simple_async_result_set_check_cancellable (simple, cancellable);
2098 
2099 	g_simple_async_result_set_op_res_gpointer (
2100 		simple, context, (GDestroyNotify) async_context_free);
2101 
2102 	g_simple_async_result_run_in_thread (
2103 		simple, (GSimpleAsyncThreadFunc)
2104 		mail_session_get_inbox_thread,
2105 		io_priority, cancellable);
2106 
2107 	g_object_unref (simple);
2108 }
2109 
2110 CamelFolder *
2111 e_mail_session_get_inbox_finish (EMailSession *session,
2112                                  GAsyncResult *result,
2113                                  GError **error)
2114 {
2115 	GSimpleAsyncResult *simple;
2116 	AsyncContext *context;
2117 
2118 	g_return_val_if_fail (
2119 		g_simple_async_result_is_valid (
2120 		result, G_OBJECT (session),
2121 		e_mail_session_get_inbox), NULL);
2122 
2123 	simple = G_SIMPLE_ASYNC_RESULT (result);
2124 	context = g_simple_async_result_get_op_res_gpointer (simple);
2125 
2126 	if (g_simple_async_result_propagate_error (simple, error))
2127 		return NULL;
2128 
2129 	g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
2130 
2131 	return g_object_ref (context->folder);
2132 }
2133 
2134 static void
2135 mail_session_get_trash_thread (GSimpleAsyncResult *simple,
2136                                EMailSession *session,
2137                                GCancellable *cancellable)
2138 {
2139 	AsyncContext *context;
2140 	GError *error = NULL;
2141 
2142 	context = g_simple_async_result_get_op_res_gpointer (simple);
2143 
2144 	context->folder = e_mail_session_get_trash_sync (
2145 		session, context->uid, cancellable, &error);
2146 
2147 	if (error != NULL)
2148 		g_simple_async_result_take_error (simple, error);
2149 }
2150 
2151 CamelFolder *
2152 e_mail_session_get_trash_sync (EMailSession *session,
2153                                const gchar *service_uid,
2154                                GCancellable *cancellable,
2155                                GError **error)
2156 {
2157 	CamelService *service;
2158 	CamelFolder *folder = NULL;
2159 
2160 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2161 	g_return_val_if_fail (service_uid != NULL, NULL);
2162 
2163 	service = camel_session_ref_service (
2164 		CAMEL_SESSION (session), service_uid);
2165 
2166 	if (service == NULL)
2167 		return NULL;
2168 
2169 	if (!CAMEL_IS_STORE (service))
2170 		goto exit;
2171 
2172 	if (!camel_service_connect_sync (service, cancellable, error))
2173 		goto exit;
2174 
2175 	folder = camel_store_get_trash_folder_sync (
2176 		CAMEL_STORE (service), cancellable, error);
2177 
2178 exit:
2179 	g_object_unref (service);
2180 
2181 	return folder;
2182 }
2183 
2184 void
2185 e_mail_session_get_trash (EMailSession *session,
2186                           const gchar *service_uid,
2187                           gint io_priority,
2188                           GCancellable *cancellable,
2189                           GAsyncReadyCallback callback,
2190                           gpointer user_data)
2191 {
2192 	GSimpleAsyncResult *simple;
2193 	AsyncContext *context;
2194 
2195 	g_return_if_fail (E_IS_MAIL_SESSION (session));
2196 	g_return_if_fail (service_uid != NULL);
2197 
2198 	context = g_slice_new0 (AsyncContext);
2199 	context->uid = g_strdup (service_uid);
2200 
2201 	simple = g_simple_async_result_new (
2202 		G_OBJECT (session), callback,
2203 		user_data, e_mail_session_get_trash);
2204 
2205 	g_simple_async_result_set_check_cancellable (simple, cancellable);
2206 
2207 	g_simple_async_result_set_op_res_gpointer (
2208 		simple, context, (GDestroyNotify) async_context_free);
2209 
2210 	g_simple_async_result_run_in_thread (
2211 		simple, (GSimpleAsyncThreadFunc)
2212 		mail_session_get_trash_thread,
2213 		io_priority, cancellable);
2214 
2215 	g_object_unref (simple);
2216 }
2217 
2218 CamelFolder *
2219 e_mail_session_get_trash_finish (EMailSession *session,
2220                                  GAsyncResult *result,
2221                                  GError **error)
2222 {
2223 	GSimpleAsyncResult *simple;
2224 	AsyncContext *context;
2225 
2226 	g_return_val_if_fail (
2227 		g_simple_async_result_is_valid (
2228 		result, G_OBJECT (session),
2229 		e_mail_session_get_trash), NULL);
2230 
2231 	simple = G_SIMPLE_ASYNC_RESULT (result);
2232 	context = g_simple_async_result_get_op_res_gpointer (simple);
2233 
2234 	if (g_simple_async_result_propagate_error (simple, error))
2235 		return NULL;
2236 
2237 	g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
2238 
2239 	return g_object_ref (context->folder);
2240 }
2241 
2242 static void
2243 mail_session_uri_to_folder_thread (GSimpleAsyncResult *simple,
2244                                    EMailSession *session,
2245                                    GCancellable *cancellable)
2246 {
2247 	AsyncContext *context;
2248 	GError *error = NULL;
2249 
2250 	context = g_simple_async_result_get_op_res_gpointer (simple);
2251 
2252 	context->folder = e_mail_session_uri_to_folder_sync (
2253 		session, context->uri, context->flags,
2254 		cancellable, &error);
2255 
2256 	if (error != NULL)
2257 		g_simple_async_result_take_error (simple, error);
2258 }
2259 
2260 CamelFolder *
2261 e_mail_session_uri_to_folder_sync (EMailSession *session,
2262                                    const gchar *folder_uri,
2263                                    CamelStoreGetFolderFlags flags,
2264                                    GCancellable *cancellable,
2265                                    GError **error)
2266 {
2267 	CamelStore *store;
2268 	CamelFolder *folder;
2269 	gchar *folder_name;
2270 	gboolean success;
2271 
2272 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2273 	g_return_val_if_fail (folder_uri != NULL, NULL);
2274 
2275 	success = e_mail_folder_uri_parse (
2276 		CAMEL_SESSION (session), folder_uri,
2277 		&store, &folder_name, error);
2278 
2279 	if (!success)
2280 		return NULL;
2281 
2282 	folder = camel_store_get_folder_sync (
2283 		store, folder_name, flags, cancellable, error);
2284 
2285 	if (folder != NULL) {
2286 		MailFolderCache *folder_cache;
2287 		folder_cache = e_mail_session_get_folder_cache (session);
2288 		mail_folder_cache_note_folder (folder_cache, folder);
2289 	}
2290 
2291 	g_free (folder_name);
2292 	g_object_unref (store);
2293 
2294 	return folder;
2295 }
2296 
2297 void
2298 e_mail_session_uri_to_folder (EMailSession *session,
2299                               const gchar *folder_uri,
2300                               CamelStoreGetFolderFlags flags,
2301                               gint io_priority,
2302                               GCancellable *cancellable,
2303                               GAsyncReadyCallback callback,
2304                               gpointer user_data)
2305 {
2306 	GSimpleAsyncResult *simple;
2307 	AsyncContext *context;
2308 
2309 	g_return_if_fail (E_IS_MAIL_SESSION (session));
2310 	g_return_if_fail (folder_uri != NULL);
2311 
2312 	context = g_slice_new0 (AsyncContext);
2313 	context->uri = g_strdup (folder_uri);
2314 	context->flags = flags;
2315 
2316 	simple = g_simple_async_result_new (
2317 		G_OBJECT (session), callback,
2318 		user_data, e_mail_session_uri_to_folder);
2319 
2320 	g_simple_async_result_set_check_cancellable (simple, cancellable);
2321 
2322 	g_simple_async_result_set_op_res_gpointer (
2323 		simple, context, (GDestroyNotify) async_context_free);
2324 
2325 	g_simple_async_result_run_in_thread (
2326 		simple, (GSimpleAsyncThreadFunc)
2327 		mail_session_uri_to_folder_thread,
2328 		io_priority, cancellable);
2329 
2330 	g_object_unref (simple);
2331 }
2332 
2333 CamelFolder *
2334 e_mail_session_uri_to_folder_finish (EMailSession *session,
2335                                      GAsyncResult *result,
2336                                      GError **error)
2337 {
2338 	GSimpleAsyncResult *simple;
2339 	AsyncContext *context;
2340 
2341 	g_return_val_if_fail (
2342 		g_simple_async_result_is_valid (
2343 		result, G_OBJECT (session),
2344 		e_mail_session_uri_to_folder), NULL);
2345 
2346 	simple = G_SIMPLE_ASYNC_RESULT (result);
2347 	context = g_simple_async_result_get_op_res_gpointer (simple);
2348 
2349 	if (g_simple_async_result_propagate_error (simple, error))
2350 		return NULL;
2351 
2352 	g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
2353 
2354 	return g_object_ref (context->folder);
2355 }
2356 
2357 gboolean
2358 e_binding_transform_service_to_source (GBinding *binding,
2359                                        const GValue *source_value,
2360                                        GValue *target_value,
2361                                        gpointer session)
2362 {
2363 	CamelService *service;
2364 	ESourceRegistry *registry;
2365 	ESource *source;
2366 	const gchar *uid;
2367 	gboolean success = FALSE;
2368 
2369 	g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
2370 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
2371 
2372 	service = g_value_get_object (source_value);
2373 
2374 	if (!CAMEL_IS_SERVICE (service))
2375 		return FALSE;
2376 
2377 	uid = camel_service_get_uid (service);
2378 	registry = e_mail_session_get_registry (session);
2379 	source = e_source_registry_ref_source (registry, uid);
2380 
2381 	if (source != NULL) {
2382 		g_value_take_object (target_value, source);
2383 		success = TRUE;
2384 	}
2385 
2386 	return success;
2387 }
2388 
2389 gboolean
2390 e_binding_transform_source_to_service (GBinding *binding,
2391                                        const GValue *source_value,
2392                                        GValue *target_value,
2393                                        gpointer session)
2394 {
2395 	CamelService *service;
2396 	ESource *source;
2397 	const gchar *uid;
2398 
2399 	g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
2400 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
2401 
2402 	source = g_value_get_object (source_value);
2403 
2404 	if (!E_IS_SOURCE (source))
2405 		return FALSE;
2406 
2407 	uid = e_source_get_uid (source);
2408 	service = camel_session_ref_service (session, uid);
2409 
2410 	if (service == NULL)
2411 		return FALSE;
2412 
2413 	g_value_take_object (target_value, service);
2414 
2415 	return TRUE;
2416 }
2417 
2418 /******************************** Legacy API *********************************/
2419 
2420 void
2421 mail_session_flush_filter_log (EMailSession *session)
2422 {
2423 	g_return_if_fail (E_IS_MAIL_SESSION (session));
2424 
2425 	if (session->priv->filter_logfile)
2426 		fflush (session->priv->filter_logfile);
2427 }
2428 
2429 const gchar *
2430 mail_session_get_data_dir (void)
2431 {
2432 	if (G_UNLIKELY (mail_data_dir == NULL))
2433 		mail_data_dir = g_build_filename (
2434 			e_get_user_data_dir (), "mail", NULL);
2435 
2436 	return mail_data_dir;
2437 }
2438 
2439 const gchar *
2440 mail_session_get_cache_dir (void)
2441 {
2442 	if (G_UNLIKELY (mail_cache_dir == NULL))
2443 		mail_cache_dir = g_build_filename (
2444 			e_get_user_cache_dir (), "mail", NULL);
2445 
2446 	return mail_cache_dir;
2447 }
2448 
2449 const gchar *
2450 mail_session_get_config_dir (void)
2451 {
2452 	if (G_UNLIKELY (mail_config_dir == NULL))
2453 		mail_config_dir = g_build_filename (
2454 			e_get_user_config_dir (), "mail", NULL);
2455 
2456 	return mail_config_dir;
2457 }
2458 
2459 CamelStore *
2460 e_mail_session_get_vfolder_store (EMailSession *session)
2461 {
2462 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2463 
2464 	return CAMEL_STORE (session->priv->vfolder_store);
2465 }
2466 
2467 EMVFolderContext *
2468 e_mail_session_create_vfolder_context (EMailSession *session)
2469 {
2470 	EMailSessionClass *class;
2471 
2472 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2473 
2474 	class = E_MAIL_SESSION_GET_CLASS (session);
2475 	g_return_val_if_fail (class->create_vfolder_context != NULL, NULL);
2476 
2477 	return class->create_vfolder_context (session);
2478 }