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