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