evolution-3.6.4/mail/e-mail-account-store.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-mail-account-store.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-account-store.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-account-store.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 
  19 #include "e-mail-account-store.h"
  20 
  21 #include <config.h>
  22 #include <glib/gstdio.h>
  23 #include <glib/gi18n-lib.h>
  24 
  25 #include <libebackend/libebackend.h>
  26 
  27 #include <e-util/e-marshal.h>
  28 #include <libevolution-utils/e-alert-dialog.h>
  29 
  30 #include <libemail-engine/mail-ops.h>
  31 
  32 #include <mail/mail-vfolder-ui.h>
  33 
  34 #define E_MAIL_ACCOUNT_STORE_GET_PRIVATE(obj) \
  35 	(G_TYPE_INSTANCE_GET_PRIVATE \
  36 	((obj), E_TYPE_MAIL_ACCOUNT_STORE, EMailAccountStorePrivate))
  37 
  38 typedef struct _IndexItem IndexItem;
  39 
  40 struct _EMailAccountStorePrivate {
  41 	CamelService *default_service;
  42 	GHashTable *service_index;
  43 	gchar *sort_order_filename;
  44 	gboolean express_mode;
  45 	gpointer session;  /* weak pointer */
  46 	guint busy_count;
  47 };
  48 
  49 struct _IndexItem {
  50 	CamelService *service;
  51 	GtkTreeRowReference *reference;
  52 	gulong notify_handler_id;
  53 };
  54 
  55 enum {
  56 	PROP_0,
  57 	PROP_BUSY,
  58 	PROP_DEFAULT_SERVICE,
  59 	PROP_EXPRESS_MODE,
  60 	PROP_SESSION
  61 };
  62 
  63 enum {
  64 	SERVICE_ADDED,
  65 	SERVICE_REMOVED,
  66 	SERVICE_ENABLED,
  67 	SERVICE_DISABLED,
  68 	SERVICES_REORDERED,
  69 	REMOVE_REQUESTED,
  70 	ENABLE_REQUESTED,
  71 	DISABLE_REQUESTED,
  72 	LAST_SIGNAL
  73 };
  74 
  75 static guint signals[LAST_SIGNAL];
  76 
  77 /* Forward Declarations */
  78 static void	e_mail_account_store_interface_init
  79 						(GtkTreeModelIface *interface);
  80 
  81 G_DEFINE_TYPE_WITH_CODE (
  82 	EMailAccountStore,
  83 	e_mail_account_store,
  84 	GTK_TYPE_LIST_STORE,
  85 	G_IMPLEMENT_INTERFACE (
  86 		GTK_TYPE_TREE_MODEL,
  87 		e_mail_account_store_interface_init)
  88 	G_IMPLEMENT_INTERFACE (
  89 		E_TYPE_EXTENSIBLE, NULL))
  90 
  91 static void
  92 index_item_free (IndexItem *item)
  93 {
  94 	g_signal_handler_disconnect (
  95 		item->service, item->notify_handler_id);
  96 
  97 	g_object_unref (item->service);
  98 	gtk_tree_row_reference_free (item->reference);
  99 
 100 	g_slice_free (IndexItem, item);
 101 }
 102 
 103 static gboolean
 104 mail_account_store_get_iter (EMailAccountStore *store,
 105                              CamelService *service,
 106                              GtkTreeIter *iter)
 107 {
 108 	IndexItem *item;
 109 	GtkTreeModel *model;
 110 	GtkTreePath *path;
 111 	gboolean iter_set;
 112 
 113 	g_return_val_if_fail (service != NULL, FALSE);
 114 
 115 	item = g_hash_table_lookup (store->priv->service_index, service);
 116 
 117 	if (item == NULL)
 118 		return FALSE;
 119 
 120 	if (!gtk_tree_row_reference_valid (item->reference))
 121 		return FALSE;
 122 
 123 	model = gtk_tree_row_reference_get_model (item->reference);
 124 	path = gtk_tree_row_reference_get_path (item->reference);
 125 	iter_set = gtk_tree_model_get_iter (model, iter, path);
 126 	gtk_tree_path_free (path);
 127 
 128 	return iter_set;
 129 }
 130 
 131 static gint
 132 mail_account_store_default_compare (CamelService *service_a,
 133                                     CamelService *service_b,
 134                                     EMailAccountStore *store)
 135 {
 136 	const gchar *display_name_a;
 137 	const gchar *display_name_b;
 138 	const gchar *uid_a;
 139 	const gchar *uid_b;
 140 
 141 	uid_a = camel_service_get_uid (service_a);
 142 	uid_b = camel_service_get_uid (service_b);
 143 
 144 	/* Check for special cases first. */
 145 
 146 	if (e_mail_account_store_get_express_mode (store)) {
 147 		if (g_str_equal (uid_a, E_MAIL_SESSION_LOCAL_UID) &&
 148 		    g_str_equal (uid_b, E_MAIL_SESSION_VFOLDER_UID))
 149 			return -1;
 150 		else if (g_str_equal (uid_b, E_MAIL_SESSION_LOCAL_UID) &&
 151 			 g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
 152 			return 1;
 153 		else if (g_str_equal (uid_a, E_MAIL_SESSION_LOCAL_UID))
 154 			return 1;
 155 		else if (g_str_equal (uid_b, E_MAIL_SESSION_LOCAL_UID))
 156 			return -1;
 157 		else if (g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
 158 			return 1;
 159 		else if (g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
 160 			return -1;
 161 	} else {
 162 		if (g_str_equal (uid_a, E_MAIL_SESSION_LOCAL_UID))
 163 			return -1;
 164 		else if (g_str_equal (uid_b, E_MAIL_SESSION_LOCAL_UID))
 165 			return 1;
 166 		else if (g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
 167 			return 1;
 168 		else if (g_str_equal (uid_b, E_MAIL_SESSION_VFOLDER_UID))
 169 			return -1;
 170 	}
 171 
 172 	/* Otherwise sort them alphabetically. */
 173 
 174 	display_name_a = camel_service_get_display_name (service_a);
 175 	display_name_b = camel_service_get_display_name (service_b);
 176 
 177 	if (display_name_a == NULL)
 178 		display_name_a = "";
 179 
 180 	if (display_name_b == NULL)
 181 		display_name_b = "";
 182 
 183 	return g_utf8_collate (display_name_a, display_name_b);
 184 }
 185 
 186 static void
 187 mail_account_store_update_row (EMailAccountStore *store,
 188                                CamelService *service,
 189                                GtkTreeIter *iter)
 190 {
 191 	CamelProvider *provider;
 192 	gboolean is_default;
 193 	const gchar *backend_name;
 194 	const gchar *display_name;
 195 
 196 	is_default = (service == store->priv->default_service);
 197 	display_name = camel_service_get_display_name (service);
 198 
 199 	provider = camel_service_get_provider (service);
 200 	backend_name = (provider != NULL) ? provider->protocol : NULL;
 201 
 202 	gtk_list_store_set (
 203 		GTK_LIST_STORE (store), iter,
 204 		E_MAIL_ACCOUNT_STORE_COLUMN_DEFAULT, is_default,
 205 		E_MAIL_ACCOUNT_STORE_COLUMN_BACKEND_NAME, backend_name,
 206 		E_MAIL_ACCOUNT_STORE_COLUMN_DISPLAY_NAME, display_name,
 207 		-1);
 208 }
 209 
 210 static void
 211 mail_account_store_service_notify_cb (CamelService *service,
 212                                       GParamSpec *pspec,
 213                                       EMailAccountStore *store)
 214 {
 215 	GtkTreeIter iter;
 216 
 217 	if (mail_account_store_get_iter (store, service, &iter))
 218 		mail_account_store_update_row (store, service, &iter);
 219 }
 220 
 221 static void
 222 mail_account_store_remove_source_cb (ESource *source,
 223                                      GAsyncResult *result,
 224                                      EMailAccountStore *store)
 225 {
 226 	GError *error = NULL;
 227 
 228 	/* FIXME EMailAccountStore should implement EAlertSink. */
 229 	if (!e_source_remove_finish (source, result, &error)) {
 230 		g_warning ("%s: %s", G_STRFUNC, error->message);
 231 		g_error_free (error);
 232 	}
 233 
 234 	g_return_if_fail (store->priv->busy_count > 0);
 235 	store->priv->busy_count--;
 236 	g_object_notify (G_OBJECT (store), "busy");
 237 
 238 	g_object_unref (store);
 239 }
 240 
 241 static void
 242 mail_account_store_write_source_cb (ESource *source,
 243                                     GAsyncResult *result,
 244                                     EMailAccountStore *store)
 245 {
 246 	GError *error = NULL;
 247 
 248 	/* FIXME EMailAccountStore should implement EAlertSink. */
 249 	if (!e_source_write_finish (source, result, &error)) {
 250 		g_warning ("%s: %s", G_STRFUNC, error->message);
 251 		g_error_free (error);
 252 	}
 253 
 254 	g_return_if_fail (store->priv->busy_count > 0);
 255 	store->priv->busy_count--;
 256 	g_object_notify (G_OBJECT (store), "busy");
 257 
 258 	g_object_unref (store);
 259 }
 260 
 261 static void
 262 mail_account_store_clean_index (EMailAccountStore *store)
 263 {
 264 	GQueue trash = G_QUEUE_INIT;
 265 	GHashTable *hash_table;
 266 	GHashTableIter iter;
 267 	gpointer key, value;
 268 
 269 	hash_table = store->priv->service_index;
 270 	g_hash_table_iter_init (&iter, hash_table);
 271 
 272 	/* Remove index items with invalid GtkTreeRowReferences. */
 273 
 274 	while (g_hash_table_iter_next (&iter, &key, &value)) {
 275 		IndexItem *item = value;
 276 
 277 		if (!gtk_tree_row_reference_valid (item->reference))
 278 			g_queue_push_tail (&trash, key);
 279 	}
 280 
 281 	while ((key = g_queue_pop_head (&trash)) != NULL)
 282 		g_hash_table_remove (hash_table, key);
 283 }
 284 
 285 static void
 286 mail_account_store_update_index (EMailAccountStore *store,
 287                                  GtkTreePath *path,
 288                                  GtkTreeIter *iter)
 289 {
 290 	CamelService *service = NULL;
 291 	GHashTable *hash_table;
 292 	GtkTreeModel *model;
 293 	IndexItem *item;
 294 
 295 	model = GTK_TREE_MODEL (store);
 296 	hash_table = store->priv->service_index;
 297 
 298 	gtk_tree_model_get (
 299 		model, iter,
 300 		E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE, &service, -1);
 301 
 302 	if (service == NULL)
 303 		return;
 304 
 305 	item = g_hash_table_lookup (hash_table, service);
 306 
 307 	if (item == NULL) {
 308 		item = g_slice_new0 (IndexItem);
 309 		item->service = g_object_ref (service);
 310 
 311 		item->notify_handler_id = g_signal_connect (
 312 			service, "notify", G_CALLBACK (
 313 			mail_account_store_service_notify_cb), store);
 314 
 315 		g_hash_table_insert (hash_table, item->service, item);
 316 	}
 317 
 318 	/* Update the row reference so the IndexItem will survive
 319 	 * drag-and-drop (new row is inserted, old row is deleted). */
 320 	gtk_tree_row_reference_free (item->reference);
 321 	item->reference = gtk_tree_row_reference_new (model, path);
 322 
 323 	g_object_unref (service);
 324 }
 325 
 326 static void
 327 mail_account_store_set_session (EMailAccountStore *store,
 328                                 EMailSession *session)
 329 {
 330 	g_return_if_fail (E_IS_MAIL_SESSION (session));
 331 	g_return_if_fail (store->priv->session == NULL);
 332 
 333 	store->priv->session = session;
 334 
 335 	g_object_add_weak_pointer (
 336 		G_OBJECT (store->priv->session),
 337 		&store->priv->session);
 338 }
 339 
 340 static void
 341 mail_account_store_set_property (GObject *object,
 342                                  guint property_id,
 343                                  const GValue *value,
 344                                  GParamSpec *pspec)
 345 {
 346 	switch (property_id) {
 347 		case PROP_DEFAULT_SERVICE:
 348 			e_mail_account_store_set_default_service (
 349 				E_MAIL_ACCOUNT_STORE (object),
 350 				g_value_get_object (value));
 351 			return;
 352 
 353 		case PROP_EXPRESS_MODE:
 354 			e_mail_account_store_set_express_mode (
 355 				E_MAIL_ACCOUNT_STORE (object),
 356 				g_value_get_boolean (value));
 357 			return;
 358 
 359 		case PROP_SESSION:
 360 			mail_account_store_set_session (
 361 				E_MAIL_ACCOUNT_STORE (object),
 362 				g_value_get_object (value));
 363 			return;
 364 	}
 365 
 366 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 367 }
 368 
 369 static void
 370 mail_account_store_get_property (GObject *object,
 371                                  guint property_id,
 372                                  GValue *value,
 373                                  GParamSpec *pspec)
 374 {
 375 	switch (property_id) {
 376 		case PROP_BUSY:
 377 			g_value_set_boolean (
 378 				value,
 379 				e_mail_account_store_get_busy (
 380 				E_MAIL_ACCOUNT_STORE (object)));
 381 			return;
 382 
 383 		case PROP_DEFAULT_SERVICE:
 384 			g_value_set_object (
 385 				value,
 386 				e_mail_account_store_get_default_service (
 387 				E_MAIL_ACCOUNT_STORE (object)));
 388 			return;
 389 
 390 		case PROP_EXPRESS_MODE:
 391 			g_value_set_boolean (
 392 				value,
 393 				e_mail_account_store_get_express_mode (
 394 				E_MAIL_ACCOUNT_STORE (object)));
 395 			return;
 396 
 397 		case PROP_SESSION:
 398 			g_value_set_object (
 399 				value,
 400 				e_mail_account_store_get_session (
 401 				E_MAIL_ACCOUNT_STORE (object)));
 402 			return;
 403 	}
 404 
 405 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 406 }
 407 
 408 static void
 409 mail_account_store_dispose (GObject *object)
 410 {
 411 	EMailAccountStorePrivate *priv;
 412 
 413 	priv = E_MAIL_ACCOUNT_STORE_GET_PRIVATE (object);
 414 
 415 	if (priv->session != NULL) {
 416 		g_object_remove_weak_pointer (
 417 			G_OBJECT (priv->session), &priv->session);
 418 		priv->session = NULL;
 419 	}
 420 
 421 	if (priv->default_service != NULL) {
 422 		g_object_unref (priv->default_service);
 423 		priv->default_service = NULL;
 424 	}
 425 
 426 	g_hash_table_remove_all (priv->service_index);
 427 
 428 	/* Chain up to parent's dispose() method. */
 429 	G_OBJECT_CLASS (e_mail_account_store_parent_class)->dispose (object);
 430 }
 431 
 432 static void
 433 mail_account_store_finalize (GObject *object)
 434 {
 435 	EMailAccountStorePrivate *priv;
 436 
 437 	priv = E_MAIL_ACCOUNT_STORE_GET_PRIVATE (object);
 438 
 439 	g_warn_if_fail (priv->busy_count == 0);
 440 	g_hash_table_destroy (priv->service_index);
 441 	g_free (priv->sort_order_filename);
 442 
 443 	/* Chain up to parent's finalize() method. */
 444 	G_OBJECT_CLASS (e_mail_account_store_parent_class)->finalize (object);
 445 }
 446 
 447 static void
 448 mail_account_store_constructed (GObject *object)
 449 {
 450 	EMailAccountStore *store;
 451 	EMailSession *session;
 452 	ESourceRegistry *registry;
 453 	const gchar *config_dir;
 454 
 455 	/* Chain up to parent's constructed() method. */
 456 	G_OBJECT_CLASS (e_mail_account_store_parent_class)->constructed (object);
 457 
 458 	store = E_MAIL_ACCOUNT_STORE (object);
 459 	session = e_mail_account_store_get_session (store);
 460 	registry = e_mail_session_get_registry (session);
 461 
 462 	/* Bind the default mail account ESource to our default
 463 	 * CamelService, with help from some transform functions. */
 464 	g_object_bind_property_full (
 465 		registry, "default-mail-account",
 466 		store, "default-service",
 467 		G_BINDING_BIDIRECTIONAL |
 468 		G_BINDING_SYNC_CREATE,
 469 		e_binding_transform_source_to_service,
 470 		e_binding_transform_service_to_source,
 471 		session, (GDestroyNotify) NULL);
 472 
 473 	config_dir = mail_session_get_config_dir ();
 474 
 475 	/* XXX Should we take the filename as a constructor property? */
 476 	store->priv->sort_order_filename = g_build_filename (
 477 		config_dir, "sortorder.ini", NULL);
 478 
 479 	e_extensible_load_extensions (E_EXTENSIBLE (object));
 480 }
 481 
 482 static void
 483 mail_account_store_service_added (EMailAccountStore *store,
 484                                   CamelService *service)
 485 {
 486 	/* Placeholder so subclasses can safely chain up. */
 487 }
 488 
 489 static void
 490 mail_account_store_service_removed (EMailAccountStore *store,
 491                                     CamelService *service)
 492 {
 493 	EMailSession *session;
 494 	MailFolderCache *cache;
 495 	ESourceRegistry *registry;
 496 	ESource *source;
 497 	const gchar *uid;
 498 
 499 	session = e_mail_account_store_get_session (store);
 500 	cache = e_mail_session_get_folder_cache (session);
 501 
 502 	mail_folder_cache_service_removed (cache, service);
 503 
 504 	uid = camel_service_get_uid (service);
 505 	registry = e_mail_session_get_registry (session);
 506 	source = e_source_registry_ref_source (registry, uid);
 507 
 508 	/* If this ESource is part of a collection, we need to remove
 509 	 * the entire collection.  Check the ESource and its ancestors
 510 	 * for a collection extension and remove the containing source. */
 511 	if (source != NULL) {
 512 		ESource *collection;
 513 
 514 		collection = e_source_registry_find_extension (
 515 			registry, source, E_SOURCE_EXTENSION_COLLECTION);
 516 		if (collection != NULL) {
 517 			g_object_unref (source);
 518 			source = collection;
 519 		}
 520 	}
 521 
 522 	if (source != NULL && e_source_get_removable (source)) {
 523 		store->priv->busy_count++;
 524 		g_object_notify (G_OBJECT (store), "busy");
 525 
 526 		/* XXX Should this be cancellable? */
 527 		e_source_remove (
 528 			source, NULL, (GAsyncReadyCallback)
 529 			mail_account_store_remove_source_cb,
 530 			g_object_ref (store));
 531 
 532 		g_object_unref (source);
 533 	}
 534 }
 535 
 536 static void
 537 mail_account_store_service_enabled (EMailAccountStore *store,
 538                                     CamelService *service)
 539 {
 540 	EMailSession *session;
 541 	MailFolderCache *cache;
 542 	ESourceRegistry *registry;
 543 	ESource *source;
 544 	const gchar *uid;
 545 
 546 	session = e_mail_account_store_get_session (store);
 547 	cache = e_mail_session_get_folder_cache (session);
 548 
 549 	mail_folder_cache_service_enabled (cache, service);
 550 
 551 	uid = camel_service_get_uid (service);
 552 	registry = e_mail_session_get_registry (session);
 553 	source = e_source_registry_ref_source (registry, uid);
 554 
 555 	/* Locate the identity source referenced in the [Mail Account]
 556 	 * extension.  We want to keep its enabled state synchronized
 557 	 * with the account's enabled state.  (Need to do this before
 558 	 * we swap the mail account ESource for a collection below.) */
 559 	if (source != NULL) {
 560 		ESource *identity = NULL;
 561 		ESourceMailAccount *extension;
 562 		const gchar *extension_name;
 563 
 564 		extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
 565 		extension = e_source_get_extension (source, extension_name);
 566 		uid = e_source_mail_account_get_identity_uid (extension);
 567 
 568 		if (uid != NULL)
 569 			identity = e_source_registry_ref_source (registry, uid);
 570 
 571 		if (identity != NULL && e_source_get_writable (identity)) {
 572 			e_source_set_enabled (identity, TRUE);
 573 
 574 			store->priv->busy_count++;
 575 			g_object_notify (G_OBJECT (store), "busy");
 576 
 577 			/* XXX Should this be cancellable? */
 578 			e_source_write (
 579 				identity, NULL, (GAsyncReadyCallback)
 580 				mail_account_store_write_source_cb,
 581 				g_object_ref (store));
 582 
 583 			g_object_unref (identity);
 584 		}
 585 	}
 586 
 587 	/* If this ESource is part of a collection, we need to enable
 588 	 * the entire collection.  Check the ESource and its ancestors
 589 	 * for a collection extension and enable the containing source. */
 590 	if (source != NULL) {
 591 		ESource *collection;
 592 
 593 		collection = e_source_registry_find_extension (
 594 			registry, source, E_SOURCE_EXTENSION_COLLECTION);
 595 		if (collection != NULL) {
 596 			g_object_unref (source);
 597 			source = collection;
 598 		}
 599 	}
 600 
 601 	if (source != NULL && e_source_get_writable (source)) {
 602 		e_source_set_enabled (source, TRUE);
 603 
 604 		store->priv->busy_count++;
 605 		g_object_notify (G_OBJECT (store), "busy");
 606 
 607 		/* XXX Should this be cancellable? */
 608 		e_source_write (
 609 			source, NULL, (GAsyncReadyCallback)
 610 			mail_account_store_write_source_cb,
 611 			g_object_ref (store));
 612 
 613 		g_object_unref (source);
 614 	}
 615 }
 616 
 617 static void
 618 mail_account_store_service_disabled (EMailAccountStore *store,
 619                                      CamelService *service)
 620 {
 621 	EMailSession *session;
 622 	MailFolderCache *cache;
 623 	ESourceRegistry *registry;
 624 	ESource *source;
 625 	const gchar *uid;
 626 
 627 	session = e_mail_account_store_get_session (store);
 628 	cache = e_mail_session_get_folder_cache (session);
 629 
 630 	mail_folder_cache_service_disabled (cache, service);
 631 
 632 	uid = camel_service_get_uid (service);
 633 	registry = e_mail_session_get_registry (session);
 634 	source = e_source_registry_ref_source (registry, uid);
 635 
 636 	/* Locate the identity source referenced in the [Mail Account]
 637 	 * extension.  We want to keep its enabled state synchronized
 638 	 * with the account's enabled state.  (Need to do this before
 639 	 * we swap the mail account ESource for a collection below.) */
 640 	if (source != NULL) {
 641 		ESource *identity = NULL;
 642 		ESourceMailAccount *extension;
 643 		const gchar *extension_name;
 644 
 645 		extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
 646 		extension = e_source_get_extension (source, extension_name);
 647 		uid = e_source_mail_account_get_identity_uid (extension);
 648 
 649 		if (uid != NULL)
 650 			identity = e_source_registry_ref_source (registry, uid);
 651 
 652 		if (identity != NULL && e_source_get_writable (identity)) {
 653 			e_source_set_enabled (identity, FALSE);
 654 
 655 			store->priv->busy_count++;
 656 			g_object_notify (G_OBJECT (store), "busy");
 657 
 658 			/* XXX Should this be cancellable? */
 659 			e_source_write (
 660 				identity, NULL, (GAsyncReadyCallback)
 661 				mail_account_store_write_source_cb,
 662 				g_object_ref (store));
 663 
 664 			g_object_unref (identity);
 665 		}
 666 	}
 667 
 668 	/* If this ESource is part of a collection, we need to disable
 669 	 * the entire collection.  Check the ESource and its ancestors
 670 	 * for a collection extension and disable the containing source. */
 671 	if (source != NULL) {
 672 		ESource *collection;
 673 
 674 		collection = e_source_registry_find_extension (
 675 			registry, source, E_SOURCE_EXTENSION_COLLECTION);
 676 		if (collection != NULL) {
 677 			g_object_unref (source);
 678 			source = collection;
 679 		}
 680 	}
 681 
 682 	if (source != NULL && e_source_get_writable (source)) {
 683 		e_source_set_enabled (source, FALSE);
 684 
 685 		store->priv->busy_count++;
 686 		g_object_notify (G_OBJECT (store), "busy");
 687 
 688 		/* XXX Should this be cancellable? */
 689 		e_source_write (
 690 			source, NULL, (GAsyncReadyCallback)
 691 			mail_account_store_write_source_cb,
 692 			g_object_ref (store));
 693 
 694 		g_object_unref (source);
 695 	}
 696 }
 697 
 698 static void
 699 mail_account_store_services_reordered (EMailAccountStore *store,
 700                                        gboolean default_restored)
 701 {
 702 	/* XXX Should this be made asynchronous? */
 703 
 704 	GError *error = NULL;
 705 
 706 	if (default_restored) {
 707 		const gchar *filename;
 708 
 709 		filename = store->priv->sort_order_filename;
 710 
 711 		if (g_file_test (filename, G_FILE_TEST_EXISTS))
 712 			g_unlink (filename);
 713 
 714 		return;
 715 	}
 716 
 717 	if (!e_mail_account_store_save_sort_order (store, &error)) {
 718 		g_warning ("%s: %s", G_STRFUNC, error->message);
 719 		g_error_free (error);
 720 	}
 721 }
 722 
 723 static gboolean
 724 mail_account_store_remove_requested (EMailAccountStore *store,
 725                                      GtkWindow *parent_window,
 726                                      CamelService *service)
 727 {
 728 	gint response;
 729 
 730 	/* FIXME Need to use "mail:ask-delete-account-with-proxies" if the
 731 	 *       mail account has proxies.  But this is groupwise-specific
 732 	 *       and doesn't belong here anyway.  Think of a better idea. */
 733 
 734 	response = e_alert_run_dialog_for_args (
 735 		parent_window, "mail:ask-delete-account", NULL);
 736 
 737 	return (response == GTK_RESPONSE_YES);
 738 }
 739 
 740 static gboolean
 741 mail_account_store_enable_requested (EMailAccountStore *store,
 742                                      GtkWindow *parent_window,
 743                                      CamelService *service)
 744 {
 745 	return TRUE;
 746 }
 747 
 748 static gboolean
 749 mail_account_store_disable_requested (EMailAccountStore *store,
 750                                       GtkWindow *parent_window,
 751                                       CamelService *service)
 752 {
 753 	/* FIXME Need to check whether the account has proxies and run a
 754 	 *       "mail:ask-delete-proxy-accounts" alert dialog, but this
 755 	 *       is groupwise-specific and doesn't belong here anyway.
 756 	 *       Think of a better idea. */
 757 
 758 	return TRUE;
 759 }
 760 
 761 static void
 762 mail_account_store_row_changed (GtkTreeModel *tree_model,
 763                                 GtkTreePath *path,
 764                                 GtkTreeIter *iter)
 765 {
 766 	EMailAccountStore *store;
 767 
 768 	/* Neither GtkTreeModel nor GtkListStore implements
 769 	 * this method, so there is nothing to chain up to. */
 770 
 771 	store = E_MAIL_ACCOUNT_STORE (tree_model);
 772 	mail_account_store_update_index (store, path, iter);
 773 }
 774 
 775 static void
 776 mail_account_store_row_inserted (GtkTreeModel *tree_model,
 777                                  GtkTreePath *path,
 778                                  GtkTreeIter *iter)
 779 {
 780 	EMailAccountStore *store;
 781 
 782 	/* Neither GtkTreeModel nor GtkListStore implements
 783 	 * this method, so there is nothing to chain up to. */
 784 
 785 	store = E_MAIL_ACCOUNT_STORE (tree_model);
 786 	mail_account_store_update_index (store, path, iter);
 787 }
 788 
 789 static gboolean
 790 mail_account_store_true_proceed (GSignalInvocationHint *ihint,
 791                                  GValue *return_accumulator,
 792                                  const GValue *handler_return,
 793                                  gpointer not_used)
 794 {
 795 	gboolean proceed;
 796 
 797 	proceed = g_value_get_boolean (handler_return);
 798 	g_value_set_boolean (return_accumulator, proceed);
 799 
 800 	return proceed;
 801 }
 802 
 803 static void
 804 e_mail_account_store_class_init (EMailAccountStoreClass *class)
 805 {
 806 	GObjectClass *object_class;
 807 
 808 	g_type_class_add_private (class, sizeof (EMailAccountStorePrivate));
 809 
 810 	object_class = G_OBJECT_CLASS (class);
 811 	object_class->set_property = mail_account_store_set_property;
 812 	object_class->get_property = mail_account_store_get_property;
 813 	object_class->dispose = mail_account_store_dispose;
 814 	object_class->finalize = mail_account_store_finalize;
 815 	object_class->constructed = mail_account_store_constructed;
 816 
 817 	class->service_added = mail_account_store_service_added;
 818 	class->service_removed = mail_account_store_service_removed;
 819 	class->service_enabled = mail_account_store_service_enabled;
 820 	class->service_disabled = mail_account_store_service_disabled;
 821 	class->services_reordered = mail_account_store_services_reordered;
 822 	class->remove_requested = mail_account_store_remove_requested;
 823 	class->enable_requested = mail_account_store_enable_requested;
 824 	class->disable_requested = mail_account_store_disable_requested;
 825 
 826 	g_object_class_install_property (
 827 		object_class,
 828 		PROP_BUSY,
 829 		g_param_spec_boolean (
 830 			"busy",
 831 			"Busy",
 832 			"Whether async operations are in progress",
 833 			FALSE,
 834 			G_PARAM_READABLE |
 835 			G_PARAM_STATIC_STRINGS));
 836 
 837 	g_object_class_install_property (
 838 		object_class,
 839 		PROP_DEFAULT_SERVICE,
 840 		g_param_spec_object (
 841 			"default-service",
 842 			"Default Service",
 843 			"Default mail store",
 844 			CAMEL_TYPE_SERVICE,
 845 			G_PARAM_READWRITE |
 846 			G_PARAM_STATIC_STRINGS));
 847 
 848 	g_object_class_install_property (
 849 		object_class,
 850 		PROP_EXPRESS_MODE,
 851 		g_param_spec_boolean (
 852 			"express-mode",
 853 			"Express Mode",
 854 			"Whether express mode is enabled",
 855 			FALSE,
 856 			G_PARAM_READWRITE |
 857 			G_PARAM_STATIC_STRINGS));
 858 
 859 	g_object_class_install_property (
 860 		object_class,
 861 		PROP_SESSION,
 862 		g_param_spec_object (
 863 			"session",
 864 			"Session",
 865 			"Mail session",
 866 			E_TYPE_MAIL_SESSION,
 867 			G_PARAM_READWRITE |
 868 			G_PARAM_CONSTRUCT_ONLY |
 869 			G_PARAM_STATIC_STRINGS));
 870 
 871 	signals[SERVICE_ADDED] = g_signal_new (
 872 		"service-added",
 873 		G_TYPE_FROM_CLASS (class),
 874 		G_SIGNAL_RUN_LAST,
 875 		G_STRUCT_OFFSET (EMailAccountStoreClass, service_added),
 876 		NULL, NULL,
 877 		g_cclosure_marshal_VOID__OBJECT,
 878 		G_TYPE_NONE, 1,
 879 		CAMEL_TYPE_SERVICE);
 880 
 881 	signals[SERVICE_REMOVED] = g_signal_new (
 882 		"service-removed",
 883 		G_TYPE_FROM_CLASS (class),
 884 		G_SIGNAL_RUN_LAST,
 885 		G_STRUCT_OFFSET (EMailAccountStoreClass, service_removed),
 886 		NULL, NULL,
 887 		g_cclosure_marshal_VOID__OBJECT,
 888 		G_TYPE_NONE, 1,
 889 		CAMEL_TYPE_SERVICE);
 890 
 891 	signals[SERVICE_ENABLED] = g_signal_new (
 892 		"service-enabled",
 893 		G_TYPE_FROM_CLASS (class),
 894 		G_SIGNAL_RUN_LAST,
 895 		G_STRUCT_OFFSET (EMailAccountStoreClass, service_enabled),
 896 		NULL, NULL,
 897 		g_cclosure_marshal_VOID__OBJECT,
 898 		G_TYPE_NONE, 1,
 899 		CAMEL_TYPE_SERVICE);
 900 
 901 	signals[SERVICE_DISABLED] = g_signal_new (
 902 		"service-disabled",
 903 		G_TYPE_FROM_CLASS (class),
 904 		G_SIGNAL_RUN_LAST,
 905 		G_STRUCT_OFFSET (EMailAccountStoreClass, service_disabled),
 906 		NULL, NULL,
 907 		g_cclosure_marshal_VOID__OBJECT,
 908 		G_TYPE_NONE, 1,
 909 		CAMEL_TYPE_SERVICE);
 910 
 911 	signals[SERVICES_REORDERED] = g_signal_new (
 912 		"services-reordered",
 913 		G_TYPE_FROM_CLASS (class),
 914 		G_SIGNAL_RUN_LAST,
 915 		G_STRUCT_OFFSET (EMailAccountStoreClass, services_reordered),
 916 		NULL, NULL,
 917 		g_cclosure_marshal_VOID__BOOLEAN,
 918 		G_TYPE_NONE, 1,
 919 		G_TYPE_BOOLEAN);
 920 
 921 	signals[REMOVE_REQUESTED] = g_signal_new (
 922 		"remove-requested",
 923 		G_TYPE_FROM_CLASS (class),
 924 		G_SIGNAL_RUN_LAST,
 925 		G_STRUCT_OFFSET (EMailAccountStoreClass, remove_requested),
 926 		mail_account_store_true_proceed, NULL,
 927 		e_marshal_BOOLEAN__OBJECT_OBJECT,
 928 		G_TYPE_BOOLEAN, 2,
 929 		GTK_TYPE_WINDOW,
 930 		CAMEL_TYPE_SERVICE);
 931 
 932 	signals[ENABLE_REQUESTED] = g_signal_new (
 933 		"enable-requested",
 934 		G_TYPE_FROM_CLASS (class),
 935 		G_SIGNAL_RUN_LAST,
 936 		G_STRUCT_OFFSET (EMailAccountStoreClass, enable_requested),
 937 		mail_account_store_true_proceed, NULL,
 938 		e_marshal_BOOLEAN__OBJECT_OBJECT,
 939 		G_TYPE_BOOLEAN, 2,
 940 		GTK_TYPE_WINDOW,
 941 		CAMEL_TYPE_SERVICE);
 942 
 943 	signals[DISABLE_REQUESTED] = g_signal_new (
 944 		"disable-requested",
 945 		G_TYPE_FROM_CLASS (class),
 946 		G_SIGNAL_RUN_LAST,
 947 		G_STRUCT_OFFSET (EMailAccountStoreClass, disable_requested),
 948 		mail_account_store_true_proceed, NULL,
 949 		e_marshal_BOOLEAN__OBJECT_OBJECT,
 950 		G_TYPE_BOOLEAN, 2,
 951 		GTK_TYPE_WINDOW,
 952 		CAMEL_TYPE_SERVICE);
 953 }
 954 
 955 static void
 956 e_mail_account_store_interface_init (GtkTreeModelIface *interface)
 957 {
 958 	interface->row_changed = mail_account_store_row_changed;
 959 	interface->row_inserted = mail_account_store_row_inserted;
 960 }
 961 
 962 static void
 963 e_mail_account_store_init (EMailAccountStore *store)
 964 {
 965 	GType types[E_MAIL_ACCOUNT_STORE_NUM_COLUMNS];
 966 	GHashTable *service_index;
 967 	gint ii = 0;
 968 
 969 	service_index = g_hash_table_new_full (
 970 		(GHashFunc) g_direct_hash,
 971 		(GEqualFunc) g_direct_equal,
 972 		(GDestroyNotify) NULL,
 973 		(GDestroyNotify) index_item_free);
 974 
 975 	store->priv = E_MAIL_ACCOUNT_STORE_GET_PRIVATE (store);
 976 	store->priv->service_index = service_index;
 977 
 978 	types[ii++] = CAMEL_TYPE_SERVICE;	/* COLUMN_SERVICE */
 979 	types[ii++] = G_TYPE_BOOLEAN;		/* COLUMN_BUILTIN */
 980 	types[ii++] = G_TYPE_BOOLEAN;		/* COLUMN_ENABLED */
 981 	types[ii++] = G_TYPE_BOOLEAN;		/* COLUMN_DEFAULT */
 982 	types[ii++] = G_TYPE_STRING;		/* COLUMN_BACKEND_NAME */
 983 	types[ii++] = G_TYPE_STRING;		/* COLUMN_DISPLAY_NAME */
 984 	types[ii++] = G_TYPE_BOOLEAN;		/* COLUMN_ONLINE_ACCOUNT */
 985 	types[ii++] = G_TYPE_BOOLEAN;		/* COLUMN_ENABLED_VISIBLE */
 986 
 987 	g_assert (ii == E_MAIL_ACCOUNT_STORE_NUM_COLUMNS);
 988 
 989 	gtk_list_store_set_column_types (
 990 		GTK_LIST_STORE (store),
 991 		G_N_ELEMENTS (types), types);
 992 }
 993 
 994 EMailAccountStore *
 995 e_mail_account_store_new (EMailSession *session)
 996 {
 997 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
 998 
 999 	return g_object_new (
1000 		E_TYPE_MAIL_ACCOUNT_STORE,
1001 		"session", session, NULL);
1002 }
1003 
1004 void
1005 e_mail_account_store_clear (EMailAccountStore *store)
1006 {
1007 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1008 
1009 	gtk_list_store_clear (GTK_LIST_STORE (store));
1010 	g_hash_table_remove_all (store->priv->service_index);
1011 }
1012 
1013 gboolean
1014 e_mail_account_store_get_busy (EMailAccountStore *store)
1015 {
1016 	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1017 
1018 	return (store->priv->busy_count > 0);
1019 }
1020 
1021 EMailSession *
1022 e_mail_account_store_get_session (EMailAccountStore *store)
1023 {
1024 	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), NULL);
1025 
1026 	return E_MAIL_SESSION (store->priv->session);
1027 }
1028 
1029 CamelService *
1030 e_mail_account_store_get_default_service (EMailAccountStore *store)
1031 {
1032 	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), NULL);
1033 
1034 	return store->priv->default_service;
1035 }
1036 
1037 void
1038 e_mail_account_store_set_default_service (EMailAccountStore *store,
1039                                           CamelService *service)
1040 {
1041 	GtkTreeModel *model;
1042 	GtkTreeIter iter;
1043 	gboolean iter_set;
1044 
1045 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1046 
1047 	if (service == store->priv->default_service)
1048 		return;
1049 
1050 	if (service != NULL) {
1051 		g_return_if_fail (CAMEL_IS_SERVICE (service));
1052 		g_object_ref (service);
1053 	}
1054 
1055 	if (store->priv->default_service != NULL)
1056 		g_object_unref (store->priv->default_service);
1057 
1058 	store->priv->default_service = service;
1059 
1060 	model = GTK_TREE_MODEL (store);
1061 	iter_set = gtk_tree_model_get_iter_first (model, &iter);
1062 
1063 	while (iter_set) {
1064 		CamelService *candidate;
1065 
1066 		gtk_tree_model_get (
1067 			model, &iter,
1068 			E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE,
1069 			&candidate, -1);
1070 
1071 		gtk_list_store_set (
1072 			GTK_LIST_STORE (model), &iter,
1073 			E_MAIL_ACCOUNT_STORE_COLUMN_DEFAULT,
1074 			service == candidate, -1);
1075 
1076 		g_object_unref (candidate);
1077 
1078 		iter_set = gtk_tree_model_iter_next (model, &iter);
1079 	}
1080 
1081 	g_object_notify (G_OBJECT (store), "default-service");
1082 }
1083 
1084 gboolean
1085 e_mail_account_store_get_express_mode (EMailAccountStore *store)
1086 {
1087 	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1088 
1089 	return store->priv->express_mode;
1090 }
1091 
1092 void
1093 e_mail_account_store_set_express_mode (EMailAccountStore *store,
1094                                        gboolean express_mode)
1095 {
1096 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1097 
1098 	if (store->priv->express_mode == express_mode)
1099 		return;
1100 
1101 	store->priv->express_mode = express_mode;
1102 
1103 	g_object_notify (G_OBJECT (store), "express-mode");
1104 }
1105 
1106 void
1107 e_mail_account_store_add_service (EMailAccountStore *store,
1108                                   CamelService *service)
1109 {
1110 	EMailSession *session;
1111 	ESourceRegistry *registry;
1112 	ESource *collection;
1113 	ESource *source;
1114 	GtkTreeIter iter;
1115 	const gchar *filename;
1116 	const gchar *uid;
1117 	gboolean builtin;
1118 	gboolean enabled;
1119 	gboolean online_account = FALSE;
1120 	gboolean enabled_visible = TRUE;
1121 
1122 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1123 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1124 
1125 	/* Avoid duplicate services in the account store. */
1126 	if (mail_account_store_get_iter (store, service, &iter))
1127 		g_return_if_reached ();
1128 
1129 	uid = camel_service_get_uid (service);
1130 
1131 	builtin =
1132 		(g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0) ||
1133 		(g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0);
1134 
1135 	session = e_mail_account_store_get_session (store);
1136 
1137 	registry = e_mail_session_get_registry (session);
1138 	source = e_source_registry_ref_source (registry, uid);
1139 	g_return_if_fail (source != NULL);
1140 
1141 	/* If this ESource is part of a collection, we need to
1142 	 * pick up the enabled state for the entire collection.
1143 	 * Check the ESource and its ancestors for a collection
1144 	 * extension and read from the containing source. */
1145 	collection = e_source_registry_find_extension (
1146 		registry, source, E_SOURCE_EXTENSION_COLLECTION);
1147 	if (collection != NULL) {
1148 		const gchar *extension_name;
1149 
1150 		enabled = e_source_get_enabled (collection);
1151 
1152 		/* Check for GNOME Online Accounts linkage. */
1153 		extension_name = E_SOURCE_EXTENSION_GOA;
1154 		if (e_source_has_extension (collection, extension_name)) {
1155 			online_account = TRUE;
1156 			enabled_visible = FALSE;
1157 		}
1158 
1159 		g_object_unref (collection);
1160 	} else {
1161 		enabled = e_source_get_enabled (source);
1162 	}
1163 
1164 	g_object_unref (source);
1165 
1166 	/* Where do we insert new services now that accounts can be
1167 	 * reordered?  This is just a simple policy I came up with.
1168 	 * It's certainly subject to debate and tweaking.
1169 	 *
1170 	 * Always insert new services in row 0 initially.  Then test
1171 	 * for the presence of the sort order file.  If present, the
1172 	 * user has messed around with the ordering so leave the new
1173 	 * service at row 0.  If not present, services are sorted in
1174 	 * their default order.  So re-apply the default order using
1175 	 * e_mail_account_store_reorder_services(store, NULL) so the
1176 	 * new service moves to its proper default position. */
1177 
1178 	gtk_list_store_prepend (GTK_LIST_STORE (store), &iter);
1179 
1180 	gtk_list_store_set (
1181 		GTK_LIST_STORE (store), &iter,
1182 		E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE, service,
1183 		E_MAIL_ACCOUNT_STORE_COLUMN_BUILTIN, builtin,
1184 		E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED, enabled,
1185 		E_MAIL_ACCOUNT_STORE_COLUMN_ONLINE_ACCOUNT, online_account,
1186 		E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED_VISIBLE, enabled_visible,
1187 		-1);
1188 
1189 	/* This populates the rest of the columns. */
1190 	mail_account_store_update_row (store, service, &iter);
1191 
1192 	/* No need to connect to "service-added" emissions since it's
1193 	 * always immediately followed by either "service-enabled" or
1194 	 * "service-disabled" in MailFolderCache */
1195 
1196 	g_signal_emit (store, signals[SERVICE_ADDED], 0, service);
1197 
1198 	if (enabled)
1199 		g_signal_emit (store, signals[SERVICE_ENABLED], 0, service);
1200 	else
1201 		g_signal_emit (store, signals[SERVICE_DISABLED], 0, service);
1202 
1203 	filename = store->priv->sort_order_filename;
1204 
1205 	if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1206 		e_mail_account_store_reorder_services (store, NULL);
1207 }
1208 
1209 void
1210 e_mail_account_store_remove_service (EMailAccountStore *store,
1211                                      GtkWindow *parent_window,
1212                                      CamelService *service)
1213 {
1214 	GtkTreeIter iter;
1215 	gboolean proceed = TRUE;
1216 
1217 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1218 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1219 
1220 	/* XXX Our service_removed() class method calls e_source_remove(),
1221 	 *     which causes the registry service to emit a "source-removed"
1222 	 *     signal.  But since other applications may also induce signal
1223 	 *     emissions from the registry service, EMailUISession handles
1224 	 *     "source-removed" by calling this function.  So quietly break
1225 	 *     the cycle if we don't find the service in our tree model. */
1226 	if (!mail_account_store_get_iter (store, service, &iter))
1227 		return;
1228 
1229 	/* If no parent window was given, skip the request signal. */
1230 	if (GTK_IS_WINDOW (parent_window))
1231 		g_signal_emit (
1232 			store, signals[REMOVE_REQUESTED], 0,
1233 			parent_window, service, &proceed);
1234 
1235 	if (proceed) {
1236 		g_object_ref (service);
1237 
1238 		gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
1239 
1240 		mail_account_store_clean_index (store);
1241 
1242 		g_signal_emit (store, signals[SERVICE_REMOVED], 0, service);
1243 
1244 		g_object_unref (service);
1245 	}
1246 }
1247 
1248 void
1249 e_mail_account_store_enable_service (EMailAccountStore *store,
1250                                      GtkWindow *parent_window,
1251                                      CamelService *service)
1252 {
1253 	GtkTreeIter iter;
1254 	gboolean proceed;
1255 
1256 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1257 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1258 
1259 	if (!mail_account_store_get_iter (store, service, &iter))
1260 		g_return_if_reached ();
1261 
1262 	/* If no parent window was given, skip the request signal. */
1263 	if (GTK_IS_WINDOW (parent_window))
1264 		g_signal_emit (
1265 			store, signals[ENABLE_REQUESTED], 0,
1266 			parent_window, service, &proceed);
1267 
1268 	if (proceed) {
1269 		gtk_list_store_set (
1270 			GTK_LIST_STORE (store), &iter,
1271 			E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED, TRUE, -1);
1272 		g_signal_emit (store, signals[SERVICE_ENABLED], 0, service);
1273 	}
1274 }
1275 
1276 void
1277 e_mail_account_store_disable_service (EMailAccountStore *store,
1278                                       GtkWindow *parent_window,
1279                                       CamelService *service)
1280 {
1281 	GtkTreeIter iter;
1282 	gboolean proceed;
1283 
1284 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1285 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1286 
1287 	if (!mail_account_store_get_iter (store, service, &iter))
1288 		g_return_if_reached ();
1289 
1290 	/* If no parent window was given, skip the request signal. */
1291 	if (GTK_IS_WINDOW (parent_window))
1292 		g_signal_emit (
1293 			store, signals[DISABLE_REQUESTED], 0,
1294 			parent_window, service, &proceed);
1295 
1296 	if (proceed) {
1297 		gtk_list_store_set (
1298 			GTK_LIST_STORE (store), &iter,
1299 			E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED, FALSE, -1);
1300 		g_signal_emit (store, signals[SERVICE_DISABLED], 0, service);
1301 	}
1302 }
1303 
1304 void
1305 e_mail_account_store_queue_services (EMailAccountStore *store,
1306                                      GQueue *out_queue)
1307 {
1308 	GtkTreeModel *tree_model;
1309 	GtkTreeIter iter;
1310 	gboolean iter_set;
1311 	gint column;
1312 
1313 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1314 	g_return_if_fail (out_queue != NULL);
1315 
1316 	tree_model = GTK_TREE_MODEL (store);
1317 
1318 	iter_set = gtk_tree_model_get_iter_first (tree_model, &iter);
1319 
1320 	while (iter_set) {
1321 		GValue value = G_VALUE_INIT;
1322 
1323 		column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
1324 		gtk_tree_model_get_value (tree_model, &iter, column, &value);
1325 		g_queue_push_tail (out_queue, g_value_get_object (&value));
1326 		g_value_unset (&value);
1327 
1328 		iter_set = gtk_tree_model_iter_next (tree_model, &iter);
1329 	}
1330 }
1331 
1332 void
1333 e_mail_account_store_queue_enabled_services (EMailAccountStore *store,
1334                                              GQueue *out_queue)
1335 {
1336 	GtkTreeModel *tree_model;
1337 	GtkTreeIter iter;
1338 	gboolean iter_set;
1339 	gint column;
1340 
1341 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1342 	g_return_if_fail (out_queue != NULL);
1343 
1344 	tree_model = GTK_TREE_MODEL (store);
1345 
1346 	iter_set = gtk_tree_model_get_iter_first (tree_model, &iter);
1347 
1348 	while (iter_set) {
1349 		GValue value = G_VALUE_INIT;
1350 		gboolean enabled;
1351 
1352 		column = E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED;
1353 		gtk_tree_model_get_value (tree_model, &iter, column, &value);
1354 		enabled = g_value_get_boolean (&value);
1355 		g_value_unset (&value);
1356 
1357 		if (enabled) {
1358 			column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
1359 			gtk_tree_model_get_value (tree_model, &iter, column, &value);
1360 			g_queue_push_tail (out_queue, g_value_get_object (&value));
1361 			g_value_unset (&value);
1362 		}
1363 
1364 		iter_set = gtk_tree_model_iter_next (tree_model, &iter);
1365 	}
1366 }
1367 
1368 gboolean
1369 e_mail_account_store_have_enabled_service (EMailAccountStore *store,
1370                                            GType service_type)
1371 {
1372 	GtkTreeModel *tree_model;
1373 	GtkTreeIter iter;
1374 	gboolean iter_set;
1375 	gint column;
1376 	gboolean found = FALSE;
1377 
1378 	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1379 
1380 	tree_model = GTK_TREE_MODEL (store);
1381 
1382 	iter_set = gtk_tree_model_get_iter_first (tree_model, &iter);
1383 
1384 	while (iter_set && !found) {
1385 		GValue value = G_VALUE_INIT;
1386 		gboolean enabled;
1387 
1388 		column = E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED;
1389 		gtk_tree_model_get_value (tree_model, &iter, column, &value);
1390 		enabled = g_value_get_boolean (&value);
1391 		g_value_unset (&value);
1392 
1393 		if (enabled) {
1394 			CamelService *service;
1395 
1396 			column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
1397 			gtk_tree_model_get_value (tree_model, &iter, column, &value);
1398 			service = g_value_get_object (&value);
1399 			found = service && G_TYPE_CHECK_INSTANCE_TYPE (service, service_type);
1400 			g_value_unset (&value);
1401 		}
1402 
1403 		iter_set = gtk_tree_model_iter_next (tree_model, &iter);
1404 	}
1405 
1406 	return found;
1407 }
1408 
1409 void
1410 e_mail_account_store_reorder_services (EMailAccountStore *store,
1411                                        GQueue *ordered_services)
1412 {
1413 	GQueue *current_order = NULL;
1414 	GQueue *default_order = NULL;
1415 	GtkTreeModel *tree_model;
1416 	gboolean use_default_order;
1417 	GList *head, *link;
1418 	gint *new_order;
1419 	gint n_children;
1420 	gint new_pos = 0;
1421 
1422 	g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1423 
1424 	tree_model = GTK_TREE_MODEL (store);
1425 	n_children = gtk_tree_model_iter_n_children (tree_model, NULL);
1426 
1427 	/* Treat NULL queues and empty queues the same. */
1428 	if (ordered_services != NULL && g_queue_is_empty (ordered_services))
1429 		ordered_services = NULL;
1430 
1431 	/* If the length of the custom ordering disagrees with the
1432 	 * number of rows in the store, revert to default ordering. */
1433 	if (ordered_services != NULL) {
1434 		if (g_queue_get_length (ordered_services) != n_children)
1435 			ordered_services = NULL;
1436 	}
1437 
1438 	use_default_order = (ordered_services == NULL);
1439 
1440 	/* Build a queue of CamelServices in the order they appear in
1441 	 * the list store.  We'll use this to construct the mapping to
1442 	 * pass to gtk_list_store_reorder(). */
1443 	current_order = g_queue_new ();
1444 	e_mail_account_store_queue_services (store, current_order);
1445 
1446 	/* If a custom ordering was not given, revert to default. */
1447 	if (use_default_order) {
1448 		default_order = g_queue_copy (current_order);
1449 
1450 		g_queue_sort (
1451 			default_order, (GCompareDataFunc)
1452 			mail_account_store_default_compare, store);
1453 
1454 		ordered_services = default_order;
1455 	}
1456 
1457 	new_order = g_new0 (gint, n_children);
1458 	head = g_queue_peek_head_link (ordered_services);
1459 
1460 	for (link = head; link != NULL; link = g_list_next (link)) {
1461 		GList *matching_link;
1462 		gint old_pos;
1463 
1464 		matching_link = g_queue_find (current_order, link->data);
1465 
1466 		if (matching_link == NULL || matching_link->data == NULL)
1467 			break;
1468 
1469 		old_pos = g_queue_link_index (current_order, matching_link);
1470 
1471 		matching_link->data = NULL;
1472 		new_order[new_pos++] = old_pos;
1473 	}
1474 
1475 	if (new_pos == n_children) {
1476 		gtk_list_store_reorder (GTK_LIST_STORE (store), new_order);
1477 		g_signal_emit (
1478 			store, signals[SERVICES_REORDERED], 0,
1479 			use_default_order);
1480 	}
1481 
1482 	g_free (new_order);
1483 
1484 	if (current_order != NULL)
1485 		g_queue_free (current_order);
1486 
1487 	if (default_order != NULL)
1488 		g_queue_free (default_order);
1489 }
1490 
1491 gint
1492 e_mail_account_store_compare_services (EMailAccountStore *store,
1493                                        CamelService *service_a,
1494                                        CamelService *service_b)
1495 {
1496 	GtkTreeModel *model;
1497 	GtkTreePath *path_a;
1498 	GtkTreePath *path_b;
1499 	GtkTreeIter iter_a;
1500 	GtkTreeIter iter_b;
1501 	gboolean iter_a_set;
1502 	gboolean iter_b_set;
1503 	gint result;
1504 
1505 	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), -1);
1506 	g_return_val_if_fail (CAMEL_IS_SERVICE (service_a), -1);
1507 	g_return_val_if_fail (CAMEL_IS_SERVICE (service_b), -1);
1508 
1509 	/* XXX This is horribly inefficient but should be
1510 	 *     over a small enough set to not be noticable. */
1511 
1512 	iter_a_set = mail_account_store_get_iter (store, service_a, &iter_a);
1513 	iter_b_set = mail_account_store_get_iter (store, service_b, &iter_b);
1514 
1515 	if (!iter_a_set && !iter_b_set)
1516 		return 0;
1517 
1518 	if (!iter_a_set)
1519 		return -1;
1520 
1521 	if (!iter_b_set)
1522 		return 1;
1523 
1524 	model = GTK_TREE_MODEL (store);
1525 
1526 	path_a = gtk_tree_model_get_path (model, &iter_a);
1527 	path_b = gtk_tree_model_get_path (model, &iter_b);
1528 
1529 	result = gtk_tree_path_compare (path_a, path_b);
1530 
1531 	gtk_tree_path_free (path_a);
1532 	gtk_tree_path_free (path_b);
1533 
1534 	return result;
1535 }
1536 
1537 gboolean
1538 e_mail_account_store_load_sort_order (EMailAccountStore *store,
1539                                       GError **error)
1540 {
1541 	GQueue service_queue = G_QUEUE_INIT;
1542 	EMailSession *session;
1543 	GKeyFile *key_file;
1544 	const gchar *filename;
1545 	gchar **service_uids;
1546 	gboolean success = TRUE;
1547 	gsize ii, length;
1548 
1549 	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1550 
1551 	session = e_mail_account_store_get_session (store);
1552 
1553 	key_file = g_key_file_new ();
1554 	filename = store->priv->sort_order_filename;
1555 
1556 	if (g_file_test (filename, G_FILE_TEST_EXISTS))
1557 		success = g_key_file_load_from_file (
1558 			key_file, filename, G_KEY_FILE_NONE, error);
1559 
1560 	if (!success) {
1561 		g_key_file_free (key_file);
1562 		return FALSE;
1563 	}
1564 
1565 	/* If the key is not present, length is set to zero. */
1566 	service_uids = g_key_file_get_string_list (
1567 		key_file, "Accounts", "SortOrder", &length, NULL);
1568 
1569 	for (ii = 0; ii < length; ii++) {
1570 		CamelService *service;
1571 
1572 		service = camel_session_ref_service (
1573 			CAMEL_SESSION (session), service_uids[ii]);
1574 		if (service != NULL)
1575 			g_queue_push_tail (&service_queue, service);
1576 	}
1577 
1578 	e_mail_account_store_reorder_services (store, &service_queue);
1579 
1580 	while (!g_queue_is_empty (&service_queue))
1581 		g_object_unref (g_queue_pop_head (&service_queue));
1582 
1583 	g_strfreev (service_uids);
1584 
1585 	g_key_file_free (key_file);
1586 
1587 	return TRUE;
1588 }
1589 
1590 gboolean
1591 e_mail_account_store_save_sort_order (EMailAccountStore *store,
1592                                       GError **error)
1593 {
1594 	GKeyFile *key_file;
1595 	GtkTreeModel *model;
1596 	GtkTreeIter iter;
1597 	const gchar **service_uids;
1598 	const gchar *filename;
1599 	gchar *contents;
1600 	gboolean iter_set;
1601 	gboolean success;
1602 	gsize length;
1603 	gsize ii = 0;
1604 
1605 	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1606 
1607 	model = GTK_TREE_MODEL (store);
1608 	length = gtk_tree_model_iter_n_children (model, NULL);
1609 
1610 	/* Empty store, nothing to save. */
1611 	if (length == 0)
1612 		return TRUE;
1613 
1614 	service_uids = g_new0 (const gchar *, length);
1615 
1616 	iter_set = gtk_tree_model_get_iter_first (model, &iter);
1617 
1618 	while (iter_set) {
1619 		GValue value = G_VALUE_INIT;
1620 		const gint column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
1621 		CamelService *service;
1622 
1623 		gtk_tree_model_get_value (model, &iter, column, &value);
1624 		service = g_value_get_object (&value);
1625 		service_uids[ii++] = camel_service_get_uid (service);
1626 		g_value_unset (&value);
1627 
1628 		iter_set = gtk_tree_model_iter_next (model, &iter);
1629 	}
1630 
1631 	key_file = g_key_file_new ();
1632 	filename = store->priv->sort_order_filename;
1633 
1634 	g_key_file_set_string_list (
1635 		key_file, "Accounts", "SortOrder", service_uids, length);
1636 
1637 	contents = g_key_file_to_data (key_file, &length, NULL);
1638 	success = g_file_set_contents (filename, contents, length, error);
1639 	g_free (contents);
1640 
1641 	g_key_file_free (key_file);
1642 
1643 	g_free (service_uids);
1644 
1645 	return success;
1646 }