1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <glib/gi18n.h>
33 #include <tny-error.h>
34 #include <tny-account.h>
35 #include <tny-account-store.h>
36 #include <tny-store-account.h>
37 #include <tny-transport-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-account-store.h>
40 #include <tny-camel-transport-account.h>
41 #include <tny-camel-imap-store-account.h>
42 #include <tny-camel-pop-store-account.h>
43 #include "modest-text-utils.h"
44 #include <modest-runtime.h>
45 #include <modest-marshal.h>
46 #include <modest-protocol-registry.h>
47 #include <modest-local-folder-info.h>
48 #include "modest-account-protocol.h"
49 #include <modest-tny-account.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-account-mgr.h>
52 #include <modest-account-mgr-helpers.h>
53 #include <widgets/modest-window-mgr.h>
54 #include <modest-signal-mgr.h>
55 #include <modest-debug.h>
57 #include <modest-defs.h>
58 #include "modest-tny-account-store.h"
59 #include "modest-tny-platform-factory.h"
60 #include <tny-gtk-lockable.h>
61 #include <camel/camel.h>
62 #include <modest-platform.h>
63 #include "modest-ui-actions.h"
64 #include <widgets/modest-account-settings-dialog.h>
66 #ifdef MODEST_PLATFORM_MAEMO
67 #include <tny-maemo-conic-device.h>
68 #include <maemo/modest-maemo-utils.h>
71 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
73 /* 'private'/'protected' functions */
74 static void modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass);
75 static void modest_tny_account_store_finalize (GObject *obj);
76 static void modest_tny_account_store_instance_init (ModestTnyAccountStore *obj);
77 static void modest_tny_account_store_init (gpointer g, gpointer iface_data);
78 static void modest_tny_account_store_base_init (gpointer g_class);
80 static void on_account_inserted (ModestAccountMgr *acc_mgr,
84 static void add_existing_accounts (ModestTnyAccountStore *self);
86 static void insert_account (ModestTnyAccountStore *self,
90 static void on_account_removed (ModestAccountMgr *acc_mgr,
94 static gchar* get_password (TnyAccount *account,
95 const gchar * prompt_not_used,
98 static void forget_password (TnyAccount *account);
100 static void on_vfs_volume_mounted (GnomeVFSVolumeMonitor *volume_monitor,
101 GnomeVFSVolume *volume,
104 static void on_vfs_volume_unmounted (GnomeVFSVolumeMonitor *volume_monitor,
105 GnomeVFSVolume *volume,
108 static void forget_password_in_memory (ModestTnyAccountStore *self,
109 const gchar *server_account_name);
111 static void add_connection_specific_transport_accounts (ModestTnyAccountStore *self);
113 static void remove_connection_specific_transport_accounts (ModestTnyAccountStore *self);
115 static void connection_status_changed (TnyAccount *account,
116 TnyConnectionStatus status,
119 static inline gboolean only_local_accounts (ModestTnyAccountStore *self);
121 /* list my signals */
123 ACCOUNT_CHANGED_SIGNAL,
124 ACCOUNT_INSERTED_SIGNAL,
125 ACCOUNT_REMOVED_SIGNAL,
127 PASSWORD_REQUESTED_SIGNAL,
131 typedef struct _ModestTnyAccountStorePrivate ModestTnyAccountStorePrivate;
132 struct _ModestTnyAccountStorePrivate {
134 GHashTable *password_hash;
135 ModestAccountMgr *account_mgr;
136 TnySessionCamel *session;
141 /* We cache the lists of accounts here */
142 TnyList *store_accounts;
143 TnyList *transport_accounts;
144 TnyList *store_accounts_outboxes;
146 /* Matches transport accounts and outbox folder */
147 GHashTable *outbox_of_transport;
149 /* is sending mail blocked? */
150 gboolean send_mail_blocked;
153 #define MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
154 MODEST_TYPE_TNY_ACCOUNT_STORE, \
155 ModestTnyAccountStorePrivate))
158 static GObjectClass *parent_class = NULL;
160 static guint signals[LAST_SIGNAL] = {0};
163 modest_tny_account_store_get_type (void)
165 static GType my_type = 0;
168 static const GTypeInfo my_info = {
169 sizeof(ModestTnyAccountStoreClass),
170 modest_tny_account_store_base_init, /* base init */
171 NULL, /* base finalize */
172 (GClassInitFunc) modest_tny_account_store_class_init,
173 NULL, /* class finalize */
174 NULL, /* class data */
175 sizeof(ModestTnyAccountStore),
177 (GInstanceInitFunc) modest_tny_account_store_instance_init,
181 static const GInterfaceInfo iface_info = {
182 (GInterfaceInitFunc) modest_tny_account_store_init,
183 NULL, /* interface_finalize */
184 NULL /* interface_data */
187 my_type = g_type_register_static (G_TYPE_OBJECT,
188 "ModestTnyAccountStore",
190 g_type_add_interface_static (my_type, TNY_TYPE_ACCOUNT_STORE,
198 modest_tny_account_store_base_init (gpointer g_class)
200 static gboolean tny_account_store_initialized = FALSE;
202 if (!tny_account_store_initialized) {
204 signals[ACCOUNT_CHANGED_SIGNAL] =
205 g_signal_new ("account_changed",
206 MODEST_TYPE_TNY_ACCOUNT_STORE,
208 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_changed),
210 g_cclosure_marshal_VOID__OBJECT,
211 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
213 signals[ACCOUNT_INSERTED_SIGNAL] =
214 g_signal_new ("account_inserted",
215 MODEST_TYPE_TNY_ACCOUNT_STORE,
217 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_inserted),
219 g_cclosure_marshal_VOID__OBJECT,
220 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
222 signals[ACCOUNT_REMOVED_SIGNAL] =
223 g_signal_new ("account_removed",
224 MODEST_TYPE_TNY_ACCOUNT_STORE,
226 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_removed),
228 g_cclosure_marshal_VOID__OBJECT,
229 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
231 signals[PASSWORD_REQUESTED_SIGNAL] =
232 g_signal_new ("password_requested",
233 MODEST_TYPE_TNY_ACCOUNT_STORE,
235 G_STRUCT_OFFSET(ModestTnyAccountStoreClass, password_requested),
237 modest_marshal_VOID__STRING_POINTER_POINTER_POINTER_POINTER,
238 G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
241 tny_account_store_initialized = TRUE;
247 modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass)
249 GObjectClass *gobject_class;
250 gobject_class = (GObjectClass*) klass;
252 parent_class = g_type_class_peek_parent (klass);
253 gobject_class->finalize = modest_tny_account_store_finalize;
255 g_type_class_add_private (gobject_class,
256 sizeof(ModestTnyAccountStorePrivate));
260 modest_tny_account_store_instance_init (ModestTnyAccountStore *obj)
262 GnomeVFSVolumeMonitor* monitor = NULL;
263 ModestTnyAccountStorePrivate *priv;
265 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
267 priv->cache_dir = NULL;
268 priv->account_mgr = NULL;
269 priv->session = NULL;
271 priv->sighandlers = NULL;
272 priv->send_mail_blocked = FALSE;
274 priv->outbox_of_transport = g_hash_table_new_full (g_direct_hash,
279 /* An in-memory store of passwords,
280 * for passwords that are not remembered in the configuration,
281 * so they need to be asked for from the user once in each session:
283 priv->password_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
286 /* Respond to volume mounts and unmounts, such
287 * as the insertion/removal of the memory card: */
288 /* This is a singleton, so it does not need to be unrefed. */
289 monitor = gnome_vfs_get_volume_monitor();
291 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
294 G_CALLBACK(on_vfs_volume_mounted),
296 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
297 G_OBJECT(monitor), "volume-unmounted",
298 G_CALLBACK(on_vfs_volume_unmounted),
302 /* disconnect the list of TnyAccounts */
304 account_verify_last_ref (TnyAccount *account, const gchar *str)
308 g_return_if_fail (account && TNY_IS_ACCOUNT(account));
310 txt = g_strdup_printf ("%s: %s", str ? str : "?", tny_account_get_name(account));
311 MODEST_DEBUG_VERIFY_OBJECT_LAST_REF(G_OBJECT(account),txt);
316 foreach_account_append_to_list (gpointer data,
321 list = TNY_LIST (user_data);
322 tny_list_append (list, G_OBJECT (data));
325 /********************************************************************/
326 /* Control the state of the MMC local account */
327 /********************************************************************/
329 /** Only call this if the memory card is really mounted.
332 add_mmc_account(ModestTnyAccountStore *self, gboolean emit_insert_signal)
334 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
335 g_return_if_fail (priv->session);
337 TnyAccount *mmc_account = modest_tny_account_new_for_local_folders (priv->account_mgr,
339 g_getenv (MODEST_MMC1_VOLUMEPATH_ENV));
341 /* Add to the list of store accounts */
342 tny_list_append (priv->store_accounts, G_OBJECT (mmc_account));
344 if (emit_insert_signal) {
345 g_signal_emit (G_OBJECT (self),
346 signals [ACCOUNT_INSERTED_SIGNAL],
351 g_object_unref (mmc_account);
355 on_vfs_volume_mounted(GnomeVFSVolumeMonitor *volume_monitor,
356 GnomeVFSVolume *volume,
359 ModestTnyAccountStore *self;
360 ModestTnyAccountStorePrivate *priv;
361 gchar *volume_path_uri;
365 self = MODEST_TNY_ACCOUNT_STORE(user_data);
366 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
368 /* Check whether this was the external MMC1 card: */
369 uri = gnome_vfs_volume_get_activation_uri (volume);
371 volume_path_uri = g_strconcat (MODEST_MMC1_VOLUMEPATH_URI_PREFIX,
372 g_getenv (MODEST_MMC1_VOLUMEPATH_ENV),
374 if (uri && (!strcmp (uri, volume_path_uri))) {
375 add_mmc_account (self, TRUE /* emit the insert signal. */);
378 g_free (volume_path_uri);
383 on_vfs_volume_unmounted(GnomeVFSVolumeMonitor *volume_monitor,
384 GnomeVFSVolume *volume,
387 ModestTnyAccountStore *self;
388 ModestTnyAccountStorePrivate *priv;
390 gchar *volume_path_uri;
392 self = MODEST_TNY_ACCOUNT_STORE(user_data);
393 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
395 /* Check whether this was the external MMC1 card: */
396 uri = gnome_vfs_volume_get_activation_uri (volume);
397 volume_path_uri = g_strconcat (MODEST_MMC1_VOLUMEPATH_URI_PREFIX,
398 g_getenv (MODEST_MMC1_VOLUMEPATH_ENV),
400 if (uri && (strcmp (uri, volume_path_uri) == 0)) {
401 TnyAccount *mmc_account = NULL;
402 gboolean found = FALSE;
403 TnyIterator *iter = NULL;
405 iter = tny_list_create_iterator (priv->store_accounts);
406 while (!tny_iterator_is_done (iter) && !found) {
409 account = TNY_ACCOUNT (tny_iterator_get_current (iter));
410 if (modest_tny_account_is_memory_card_account (account)) {
412 mmc_account = g_object_ref (account);
414 g_object_unref (account);
415 tny_iterator_next (iter);
417 g_object_unref (iter);
420 /* Remove from the list */
421 tny_list_remove (priv->store_accounts, G_OBJECT (mmc_account));
423 /* Notify observers */
424 g_signal_emit (G_OBJECT (self),
425 signals [ACCOUNT_REMOVED_SIGNAL],
428 g_object_unref (mmc_account);
430 g_warning ("%s: there was no store account for the unmounted MMC",
434 g_free (volume_path_uri);
439 * forget_password_in_memory
440 * @self: a TnyAccountStore instance
441 * @account: A server account.
443 * Forget any password stored in memory for this account.
444 * For instance, this should be called when the user has changed the password in the account settings.
447 forget_password_in_memory (ModestTnyAccountStore *self,
448 const gchar * server_account_name)
450 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
452 if (server_account_name && priv->password_hash) {
453 g_hash_table_remove (priv->password_hash, server_account_name);
458 on_account_changed (ModestAccountMgr *acc_mgr,
459 const gchar *account_name,
460 TnyAccountType account_type,
463 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
464 ModestTnyAccountStorePrivate *priv;
465 TnyList* account_list;
466 gboolean found = FALSE;
467 TnyIterator *iter = NULL;
469 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
470 account_list = (account_type == TNY_ACCOUNT_TYPE_STORE ?
471 priv->store_accounts :
472 priv->transport_accounts);
474 iter = tny_list_create_iterator (account_list);
475 while (!tny_iterator_is_done (iter) && !found) {
476 TnyAccount *tny_account;
477 tny_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
479 if (!strcmp (tny_account_get_id (tny_account), account_name)) {
481 modest_tny_account_update_from_account (tny_account, get_password, forget_password);
482 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_CHANGED_SIGNAL], 0, tny_account);
484 g_object_unref (tny_account);
486 tny_iterator_next (iter);
490 g_object_unref (iter);
494 show_password_warning_only (const gchar *msg)
496 /* Show an explanatory temporary banner: */
497 if (modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()))
498 modest_platform_information_banner (NULL, NULL, msg);
502 show_wrong_password_dialog (TnyAccount *account)
504 if (g_object_get_data (G_OBJECT (account), "connection_specific") != NULL) {
505 modest_ui_actions_on_smtp_servers (NULL, NULL);
507 ModestAccountProtocol *proto;
508 ModestProtocolType proto_type;
509 const gchar *modest_account_name;
510 modest_account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
513 proto_type = modest_account_mgr_get_store_protocol (modest_runtime_get_account_mgr (),
514 modest_account_name);
515 proto = (ModestAccountProtocol *)
516 modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
519 /* Create and show the dialog */
520 if (proto && MODEST_IS_ACCOUNT_PROTOCOL (proto)) {
521 ModestAccountSettingsDialog *dialog =
522 modest_account_protocol_get_account_settings_dialog (proto, modest_account_name);
523 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog), NULL);
524 gtk_widget_show (GTK_WIDGET (dialog));
527 /* Show an explanatory temporary banner: */
528 modest_platform_information_banner (NULL, NULL, _("mcen_ib_username_pw_incorrect"));
531 /* This callback will be called by Tinymail when it needs the password
532 * from the user or the account settings.
533 * It can also call forget_password() before calling this,
534 * so that we clear wrong passwords out of our account settings.
535 * Note that TnyAccount here will be the server account. */
537 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
539 ModestTnyAccountStore *self = NULL;
540 ModestTnyAccountStorePrivate *priv;
541 gchar *username = NULL;
543 gpointer pwd_ptr = NULL;
544 gboolean already_asked = FALSE;
545 const gchar *server_account_name;
548 g_return_val_if_fail (account, NULL);
551 g_debug ("%s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
554 /* Get a reference to myself */
555 self = MODEST_TNY_ACCOUNT_STORE (g_object_get_data (G_OBJECT(account), "account_store"));
556 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
558 /* Ensure that we still have this account. It could happen
559 that a set_online was requested *before* removing an
560 account, and due to tinymail emits the get_password
561 function using a g_idle the account could be actually
562 removed *before* this function was really called */
563 url_string = tny_account_get_url_string (account);
565 TnyAccount *tmp_account;
567 tmp_account = tny_account_store_find_account (TNY_ACCOUNT_STORE (self),
575 g_object_unref (tmp_account);
578 server_account_name = tny_account_get_id (account);
579 if (!server_account_name || !self) {
580 g_warning ("modest: %s: could not retrieve account_store for account %s",
581 __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
588 /* This hash map stores passwords, including passwords that are not stored in gconf. */
589 /* Is it in the hash? if it's already there, it must be wrong... */
590 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
591 * type-punned ptrs...*/
592 already_asked = priv->password_hash &&
593 g_hash_table_lookup_extended (priv->password_hash,
596 (gpointer*)&pwd_ptr);
598 g_debug ("%s: Already asked = %d\n", __FUNCTION__, already_asked);
601 /* If the password is not already there, try ModestConf */
602 if (!already_asked) {
603 pwd = modest_account_mgr_get_server_account_password (priv->account_mgr,
604 server_account_name);
605 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
608 /* If it was already asked, it must have been wrong, so ask again */
609 if (already_asked || !pwd || strlen(pwd) == 0) {
610 gboolean settings_have_password;
611 ModestProtocolType protocol_type;
613 /* As per the UI spec, if no password was set in the account settings,
614 * ask for it now. But if the password is wrong in the account settings,
615 * then show a banner and the account settings dialog so it can be corrected:
617 settings_have_password =
618 modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
620 protocol_type = modest_tny_account_get_protocol_type (account);
622 /* Show an error and after that ask for a password */
623 if (modest_protocol_registry_protocol_type_has_tag(modest_runtime_get_protocol_registry (),
624 protocol_type, MODEST_PROTOCOL_REGISTRY_TRANSPORT_PROTOCOLS)) {
625 gchar *username = NULL, *msg = NULL;
626 username = modest_account_mgr_get_server_account_username (priv->account_mgr,
627 server_account_name);
628 if (!username || strlen(username) == 0) {
629 msg = g_strdup_printf (_("emev_ni_ui_smtp_userid_invalid"),
630 tny_account_get_name (account),
631 tny_account_get_hostname (account));
634 password = modest_account_mgr_get_server_account_password (priv->account_mgr,
635 server_account_name);
636 if (!password || strlen(password) == 0)
637 msg = g_strdup_printf (_("emev_ni_ui_smtp_passwd_invalid"),
638 tny_account_get_name (account),
639 tny_account_get_hostname (account));
641 msg = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
642 tny_account_get_hostname (account));
647 modest_platform_run_information_dialog (NULL, msg, TRUE);
654 if (settings_have_password) {
655 /* The password must be wrong, so show the account settings dialog so it can be corrected: */
656 show_wrong_password_dialog (account);
664 /* we don't have it yet. Get the password from the user */
665 const gchar* account_id = tny_account_get_id (account);
666 gboolean remember = FALSE;
671 gboolean username_known =
672 modest_account_mgr_get_server_account_username_has_succeeded(priv->account_mgr,
673 server_account_name);
674 /* If the login has ever succeeded then show a specific message */
676 msg = _CS ("ecdg_ib_set_password_incorrect");
678 msg = _("mcen_ib_username_pw_incorrect");
679 show_password_warning_only (msg);
682 /* Request password */
683 g_signal_emit (G_OBJECT (self), signals[PASSWORD_REQUESTED_SIGNAL], 0,
684 account_id, /* server_account_name */
685 &username, &pwd, cancel, &remember);
689 /* The password will be returned as the result,
690 * but we need to tell tinymail about the username too: */
692 /* WARNING: I disabled setting username as this can cause locks. Anyway,
693 * as now we have the password dialog username entry always dimmed
694 * this shouldn't be a problem */
697 /* tny_account_set_user (account, username); */
699 /* Do not save the password in gconf, because
700 * the UI spec says "The password will never
701 * be saved in the account": */
703 /* We need to dup the string even knowing that
704 it's already a dup of the contents of an
705 entry, because it if it's wrong, then camel
707 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
709 g_hash_table_remove (priv->password_hash, server_account_name);
724 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
726 g_return_if_fail (account);
728 ModestTnyAccountStorePrivate *priv;
730 gpointer pwd_ptr = NULL;
731 gboolean already_asked = FALSE;
733 const gchar *server_account_name = tny_account_get_id (account);
735 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
737 /* This hash map stores passwords, including passwords that are not stored in gconf. */
738 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
739 * type-punned ptrs...*/
740 already_asked = priv->password_hash &&
741 g_hash_table_lookup_extended (priv->password_hash,
744 (gpointer*)&pwd_ptr);
747 g_hash_table_remove (priv->password_hash, server_account_name);
755 /* tinymail calls this if the connection failed due to an incorrect password.
756 * And it seems to call this for any general connection failure. */
758 forget_password (TnyAccount *account)
760 ModestTnyAccountStore *self;
761 ModestTnyAccountStorePrivate *priv;
762 const TnyAccountStore *account_store;
766 account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
768 self = MODEST_TNY_ACCOUNT_STORE (account_store);
769 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
770 key = tny_account_get_id (account);
772 /* Do not remove the key, this will allow us to detect that we
773 have already asked for it at least once */
774 pwd = g_hash_table_lookup (priv->password_hash, key);
776 memset (pwd, 0, strlen (pwd));
777 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
780 /* Remove from configuration system */
782 modest_account_mgr_unset (priv->account_mgr,
783 key, MODEST_ACCOUNT_PASSWORD, TRUE);
788 modest_tny_account_store_finalize (GObject *obj)
790 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(obj);
791 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
793 g_free (priv->cache_dir);
794 priv->cache_dir = NULL;
796 if (priv->password_hash) {
797 g_hash_table_destroy (priv->password_hash);
798 priv->password_hash = NULL;
801 if (priv->outbox_of_transport) {
802 g_hash_table_destroy (priv->outbox_of_transport);
803 priv->outbox_of_transport = NULL;
806 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
807 priv->sighandlers = NULL;
809 if (priv->account_mgr) {
810 g_object_unref (G_OBJECT(priv->account_mgr));
811 priv->account_mgr = NULL;
815 g_object_unref (G_OBJECT(priv->device));
819 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
820 if (priv->store_accounts) {
821 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
822 g_object_unref (priv->store_accounts);
823 priv->store_accounts = NULL;
826 if (priv->transport_accounts) {
827 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
828 g_object_unref (priv->transport_accounts);
829 priv->transport_accounts = NULL;
832 if (priv->store_accounts_outboxes) {
833 g_object_unref (priv->store_accounts_outboxes);
834 priv->store_accounts_outboxes = NULL;
838 camel_object_unref (CAMEL_OBJECT(priv->session));
839 priv->session = NULL;
842 G_OBJECT_CLASS(parent_class)->finalize (obj);
846 volume_path_is_mounted (const gchar* path)
848 g_return_val_if_fail (path, FALSE);
850 gboolean result = FALSE;
851 gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
852 g_return_val_if_fail (path_as_uri, FALSE);
854 /* Get the monitor singleton: */
855 GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
857 /* This seems like a simpler way to do this, but it returns a
858 * GnomeVFSVolume even if the drive is not mounted: */
860 GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor,
861 g_getenv (MODEST_MMC1_VOLUMEPATH_ENV));
863 gnome_vfs_volume_unref(volume);
867 /* Get the mounted volumes from the monitor: */
868 GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
870 for (iter = list; iter; iter = g_list_next (iter)) {
871 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
875 gnome_vfs_volume_get_display_name (volume);
876 printf ("volume display name=%s\n", display_name);
877 g_free (display_name);
881 gnome_vfs_volume_get_activation_uri (volume);
882 /* printf (" uri=%s\n", uri); */
883 if (uri && (strcmp (uri, path_as_uri) == 0))
888 gnome_vfs_volume_unref (volume);
894 g_free (path_as_uri);
899 ModestTnyAccountStore*
900 modest_tny_account_store_new (ModestAccountMgr *account_mgr,
904 ModestTnyAccountStorePrivate *priv;
905 TnyAccount *local_account = NULL;
906 TnyLockable *lockable;
908 g_return_val_if_fail (account_mgr, NULL);
909 g_return_val_if_fail (device, NULL);
911 obj = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
912 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
914 priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
915 priv->device = g_object_ref (device);
917 priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
918 if (!priv->session) {
919 g_warning ("failed to get TnySessionCamel");
923 /* Set the ui locker */
924 lockable = tny_gtk_lockable_new ();
925 tny_session_camel_set_ui_locker (priv->session, lockable);
926 g_object_unref (lockable);
928 /* Connect signals */
929 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
930 G_OBJECT(account_mgr), "account_inserted",
931 G_CALLBACK (on_account_inserted), obj);
932 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
933 G_OBJECT(account_mgr), "account_changed",
934 G_CALLBACK (on_account_changed), obj);
935 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
936 G_OBJECT(account_mgr), "account_removed",
937 G_CALLBACK (on_account_removed), obj);
939 /* Create the lists of accounts */
940 priv->store_accounts = tny_simple_list_new ();
941 priv->transport_accounts = tny_simple_list_new ();
942 priv->store_accounts_outboxes = tny_simple_list_new ();
944 /* Create the local folders account */
946 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
947 tny_list_append (priv->store_accounts, G_OBJECT(local_account));
948 g_object_unref (local_account);
950 /* Add the other remote accounts. Do this after adding the
951 local account, because we need to add our outboxes to the
952 global OUTBOX hosted in the local account */
953 add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
955 /* Add connection-specific transport accounts if there are any
956 accounts available */
957 if (!only_local_accounts (MODEST_TNY_ACCOUNT_STORE(obj)))
958 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
960 /* This is a singleton, so it does not need to be unrefed. */
961 if (volume_path_is_mounted (g_getenv (MODEST_MMC1_VOLUMEPATH_ENV))) {
963 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */);
966 return MODEST_TNY_ACCOUNT_STORE(obj);
970 modest_tny_account_store_get_accounts (TnyAccountStore *self,
972 TnyGetAccountsRequestType request_type)
974 ModestTnyAccountStorePrivate *priv;
976 g_return_if_fail (self);
977 g_return_if_fail (TNY_IS_LIST(list));
979 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
981 switch (request_type) {
982 case TNY_ACCOUNT_STORE_BOTH:
983 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
984 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
986 case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
987 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
989 case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
990 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
993 g_return_if_reached ();
996 /* Initialize session. Why do we need this ??? */
997 tny_session_camel_set_initialized (priv->session);
1002 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
1004 ModestTnyAccountStorePrivate *priv;
1005 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1007 if (!priv->cache_dir)
1008 priv->cache_dir = g_build_filename (g_get_home_dir(),
1009 MODEST_DIR, MODEST_CACHE_DIR, NULL);
1010 return priv->cache_dir;
1015 * callers need to unref
1018 modest_tny_account_store_get_device (TnyAccountStore *self)
1020 ModestTnyAccountStorePrivate *priv;
1022 g_return_val_if_fail (self, NULL);
1024 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1027 return g_object_ref (G_OBJECT(priv->device));
1034 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1036 return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self),
1037 MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1044 modest_tny_account_store_alert (TnyAccountStore *self,
1045 TnyAccount *account,
1050 ModestProtocolType protocol_type = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID;
1051 ModestProtocol *protocol = NULL;
1052 const gchar* server_name = "";
1053 gchar *prompt = NULL;
1054 gboolean retval = TRUE;
1056 /* NOTE: account may be NULL in some cases */
1057 g_return_val_if_fail (error, FALSE);
1059 /* Get the server name: */
1061 server_name = tny_account_get_hostname (account);
1062 protocol_type = modest_tny_account_get_protocol_type (account);
1063 if (protocol_type == MODEST_PROTOCOL_REGISTRY_TYPE_INVALID){
1064 g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__,
1065 tny_account_get_id (account));
1068 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
1072 switch (error->code) {
1073 case TNY_SYSTEM_ERROR_CANCEL:
1074 /* Don't show waste the user's time by showing him a dialog telling
1075 * him that he has just cancelled something: */
1078 case TNY_SERVICE_ERROR_PROTOCOL:
1079 /* Like a BAD from IMAP (protocol error) */
1080 case TNY_SERVICE_ERROR_LOST_CONNECTION:
1081 /* Lost the connection with the service */
1082 case TNY_SERVICE_ERROR_UNAVAILABLE:
1083 /* You must be working online for this operation */
1084 case TNY_SERVICE_ERROR_CONNECT:
1086 prompt = modest_protocol_get_translation (protocol, MODEST_PROTOCOL_TRANSLATION_CONNECT_ERROR, server_name);
1089 g_return_val_if_reached (FALSE);
1093 case TNY_SERVICE_ERROR_AUTHENTICATE:
1094 /* It seems that there's no better error to show with
1095 * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1096 * may appear if there's a timeout during auth */
1098 prompt = modest_protocol_get_translation (protocol, MODEST_PROTOCOL_TRANSLATION_AUTH_ERROR, server_name);
1101 g_return_val_if_reached (FALSE);
1105 case TNY_SERVICE_ERROR_CERTIFICATE:
1106 /* We'll show the proper dialog later */
1109 case TNY_SYSTEM_ERROR_MEMORY:
1110 /* Can't allocate memory for this operation */
1112 case TNY_SERVICE_ERROR_UNKNOWN:
1115 g_debug ("Unexpected error %d", error->code);
1116 g_return_val_if_reached (FALSE);
1120 if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1121 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1123 else if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE ||
1124 error->code == TNY_SERVICE_ERROR_CONNECT) {
1126 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1128 /* Show the account dialog if it was wrong */
1129 if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE)
1130 show_wrong_password_dialog (account);
1135 g_debug ("%s: error code %d (%s", __FUNCTION__, error->code, error->message);
1145 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1147 TnyAccountStoreIface *klass;
1149 g_return_if_fail (g);
1151 klass = (TnyAccountStoreIface *)g;
1153 klass->get_accounts =
1154 modest_tny_account_store_get_accounts;
1155 klass->get_cache_dir =
1156 modest_tny_account_store_get_cache_dir;
1158 modest_tny_account_store_get_device;
1160 modest_tny_account_store_alert;
1161 klass->find_account =
1162 modest_tny_account_store_find_account_by_url;
1166 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1167 ModestTnyGetPassFunc func)
1169 /* not implemented, we use signals */
1170 g_printerr ("modest: set_get_pass_func not implemented\n");
1174 modest_tny_account_store_get_session (TnyAccountStore *self)
1176 g_return_val_if_fail (self, NULL);
1177 return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1181 get_tny_account_by (TnyList *accounts,
1182 ModestTnyAccountStoreQueryType type,
1185 TnyIterator *iter = NULL;
1186 gboolean found = FALSE;
1187 TnyAccount *retval = NULL;
1189 g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1191 if (tny_list_get_length(accounts) == 0) {
1192 g_warning ("%s: account list is empty", __FUNCTION__);
1196 iter = tny_list_create_iterator (accounts);
1197 while (!tny_iterator_is_done (iter) && !found) {
1198 TnyAccount *tmp_account = NULL;
1199 const gchar *val = NULL;
1201 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1202 if (!TNY_IS_ACCOUNT(tmp_account)) {
1203 g_warning ("%s: not a valid account", __FUNCTION__);
1209 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1210 val = tny_account_get_id (tmp_account);
1212 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1213 val = tny_account_get_url_string (tmp_account);
1217 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL &&
1218 tny_account_matches_url_string (tmp_account, str)) {
1219 retval = g_object_ref (tmp_account);
1222 if (val && str && strcmp (val, str) == 0) {
1223 retval = g_object_ref (tmp_account);
1227 g_object_unref (tmp_account);
1228 tny_iterator_next (iter);
1230 g_object_unref (iter);
1236 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self,
1237 ModestTnyAccountStoreQueryType type,
1240 TnyAccount *account = NULL;
1241 ModestTnyAccountStorePrivate *priv;
1243 g_return_val_if_fail (self, NULL);
1244 g_return_val_if_fail (str, NULL);
1246 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1248 /* Search in store accounts */
1249 account = get_tny_account_by (priv->store_accounts, type, str);
1251 /* If we already found something, no need to search the transport accounts */
1253 account = get_tny_account_by (priv->transport_accounts, type, str);
1255 /* If we already found something, no need to search the
1256 per-account outbox accounts */
1258 account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1261 /* Warn if nothing was found. This is generally unusual. */
1263 g_warning("%s: Failed to find account with %s=%s\n",
1265 (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",
1269 /* Returns a new reference to the account if found */
1275 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1276 const gchar *account_name,
1277 TnyAccountType type)
1279 ModestTnyAccountStorePrivate *priv = NULL;
1280 TnyAccount *retval = NULL;
1281 TnyList *account_list = NULL;
1282 TnyIterator *iter = NULL;
1285 g_return_val_if_fail (self, NULL);
1286 g_return_val_if_fail (account_name, NULL);
1287 g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE ||
1288 type == TNY_ACCOUNT_TYPE_TRANSPORT,
1291 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1293 account_list = (type == TNY_ACCOUNT_TYPE_STORE) ?
1294 priv->store_accounts :
1295 priv->transport_accounts;
1297 if (!account_list) {
1298 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__,
1299 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1303 /* Look for the server account */
1305 iter = tny_list_create_iterator (account_list);
1306 while (!tny_iterator_is_done (iter) && !found) {
1307 const gchar *modest_acc_name;
1308 TnyAccount *tmp_account;
1310 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1312 modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1314 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1316 retval = g_object_ref (tmp_account);
1318 /* Free and continue */
1319 g_object_unref (tmp_account);
1320 tny_iterator_next (iter);
1322 g_object_unref (iter);
1325 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1326 "Number of server accounts of this type=%d\n", __FUNCTION__,
1327 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1328 account_name, tny_list_get_length (account_list));
1331 /* Returns a new reference */
1336 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1337 ModestTnyAccountStore *self, const gchar *account_name)
1341 g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1342 g_return_val_if_fail (account_name, NULL);
1344 /* Get the current connection: */
1345 device = modest_runtime_get_device ();
1348 g_warning ("%s: could not get device", __FUNCTION__);
1352 if (!tny_device_is_online (device))
1355 #ifdef MODEST_HAVE_CONIC
1356 g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1358 TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);
1359 const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1360 /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1364 ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1368 const gchar *connection_id = con_ic_iap_get_id (connection);
1369 /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1373 /* Get the connection-specific transport acccount, if any: */
1374 ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1376 /* Check if this account has connection-specific SMTP enabled */
1377 if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1381 gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager,
1384 /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1385 if (!server_account_name) {
1386 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1389 TnyAccount* account = modest_tny_account_store_get_tny_account_by (self,
1390 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1391 server_account_name);
1393 /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1394 g_free (server_account_name);
1396 /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1397 g_object_unref (connection);
1401 return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1402 #endif /* MODEST_HAVE_CONIC */
1407 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1408 const gchar *account_name)
1410 g_return_val_if_fail (self, NULL);
1411 g_return_val_if_fail (account_name, NULL);
1413 if (!account_name || !self)
1416 /* Get the connection-specific transport acccount, if any: */
1417 /* Note: This gives us a reference: */
1418 TnyAccount *account =
1419 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1421 /* If there is no connection-specific transport account (the common case),
1422 * just get the regular transport account: */
1424 /* The special local folders don't have transport accounts. */
1425 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1428 /* Note: This gives us a reference: */
1429 account = modest_tny_account_store_get_server_account (self, account_name,
1430 TNY_ACCOUNT_TYPE_TRANSPORT);
1434 /* returns a reference. */
1439 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1441 TnyAccount *account = NULL;
1442 ModestTnyAccountStorePrivate *priv;
1446 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1448 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1451 iter = tny_list_create_iterator (priv->store_accounts);
1452 while (!tny_iterator_is_done (iter) && !found) {
1453 TnyAccount *tmp_account;
1455 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1456 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1457 account = g_object_ref (tmp_account);
1460 g_object_unref (tmp_account);
1461 tny_iterator_next (iter);
1463 g_object_unref (iter);
1465 /* Returns a new reference to the account */
1470 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1472 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1475 return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1476 MODEST_MMC_ACCOUNT_ID);
1480 /*********************************************************************************/
1482 add_existing_accounts (ModestTnyAccountStore *self)
1484 GSList *account_names = NULL, *iter = NULL;
1485 ModestTnyAccountStorePrivate *priv = NULL;
1487 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1489 /* These are account names, not server_account names */
1490 account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1492 for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1493 const gchar *account_name = (const gchar*) iter->data;
1495 /* Insert all enabled accounts without notifying */
1496 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1497 insert_account (self, account_name, FALSE);
1499 modest_account_mgr_free_account_names (account_names);
1503 connection_status_changed (TnyAccount *account,
1504 TnyConnectionStatus status,
1507 /* We do this here and not in the connection policy because we
1508 don't want to do it for every account, just for the
1509 accounts that are interactively added when modest is
1511 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1512 const gchar *account_name;
1513 ModestWindow *top_window;
1514 ModestTnyAccountStorePrivate *priv = NULL;
1516 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1518 /* Remove this handler */
1519 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1521 "connection_status_changed");
1523 /* Perform a send receive */
1524 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1525 top_window = modest_window_mgr_get_current_top (modest_runtime_get_window_mgr ());
1526 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, TRUE, top_window);
1531 create_tny_account (ModestTnyAccountStore *self,
1533 TnyAccountType type,
1536 TnyAccount *account = NULL;
1537 ModestTnyAccountStorePrivate *priv = NULL;
1539 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1541 account = modest_tny_account_new_from_account (priv->account_mgr,
1548 /* Forget any cached password for the account, so that
1549 we use a new account if any */
1550 forget_password_in_memory (self, tny_account_get_id (account));
1552 /* Install a signal handler that will refresh the
1553 account the first time it becomes online. Do this
1554 only if we're adding a new account while the
1555 program is running (we do not want to do this
1557 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1558 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1560 "connection_status_changed",
1561 G_CALLBACK (connection_status_changed),
1564 /* Set the account store */
1565 g_object_set_data (G_OBJECT(account), "account_store", self);
1567 g_printerr ("modest: failed to create account for %s\n", name);
1573 typedef struct _AddOutboxInfo {
1574 ModestTnyAccountStore *account_store;
1575 TnyAccount *transport_account;
1579 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1585 TnyIterator *iter_folders;
1586 TnyFolder *per_account_outbox;
1587 TnyAccount *local_account = NULL;
1588 AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1589 ModestTnyAccountStorePrivate *priv = NULL;
1590 ModestTnyAccountStore *self;
1592 self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1593 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1595 /* Note that this could happen if there is not enough space
1596 available on disk, then the outbox folder could not be
1598 if (tny_list_get_length (list) != 1) {
1599 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1600 tny_list_get_length (list));
1604 iter_folders = tny_list_create_iterator (list);
1605 per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1606 g_object_unref (iter_folders);
1607 g_object_unref (list);
1609 /* Add the outbox of the new per-account-local-outbox account
1610 to the global local merged OUTBOX of the local folders
1612 local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1613 modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1614 per_account_outbox);
1615 /* Add the pair to the hash table */
1616 g_hash_table_insert (priv->outbox_of_transport,
1617 info->transport_account,
1618 per_account_outbox);
1620 /* Notify that the local account changed */
1621 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1622 g_object_unref (local_account);
1623 g_object_unref (per_account_outbox);
1626 g_object_unref (info->transport_account);
1627 g_slice_free (AddOutboxInfo, info);
1632 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1633 const gchar *account_name,
1634 TnyAccount *transport_account)
1636 TnyList *folders = NULL;
1637 TnyAccount *account_outbox = NULL;
1638 ModestTnyAccountStorePrivate *priv = NULL;
1639 AddOutboxInfo *info;
1641 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1643 /* Create per account local outbox */
1645 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr,
1649 if (!G_IS_OBJECT (account_outbox)) {
1650 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1654 tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1656 /* Get the outbox folder */
1657 folders = tny_simple_list_new ();
1658 info = g_slice_new0 (AddOutboxInfo);
1659 info->account_store = self;
1660 info->transport_account = g_object_ref (transport_account);
1661 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL, FALSE,
1662 add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1663 g_object_unref (account_outbox);
1667 * This function will be used for both adding new accounts and for the
1668 * initialization. In the initialization we do not want to emit
1669 * signals so notify will be FALSE, in the case of account additions
1670 * we do want to notify the observers
1673 insert_account (ModestTnyAccountStore *self,
1674 const gchar *account,
1677 ModestTnyAccountStorePrivate *priv = NULL;
1678 TnyAccount *store_account = NULL, *transport_account = NULL;
1680 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1682 /* Get the server and the transport account */
1683 store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1684 if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1685 g_warning ("%s: failed to create store account", __FUNCTION__);
1689 transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1690 if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1691 g_warning ("%s: failed to create transport account", __FUNCTION__);
1692 g_object_unref (store_account);
1696 /* Add accounts to the lists */
1697 tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1698 tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1700 /* Create a new pseudo-account with an outbox for this
1701 transport account and add it to the global outbox
1702 in the local account */
1703 add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1705 /* Notify the observers. We do it after everything is
1708 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1709 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1713 g_object_unref (store_account);
1714 g_object_unref (transport_account);
1717 static inline gboolean
1718 only_local_accounts (ModestTnyAccountStore *self)
1720 return (modest_tny_account_store_get_num_remote_accounts (self) > 0) ? FALSE : TRUE;
1724 on_account_inserted (ModestAccountMgr *acc_mgr,
1725 const gchar *account,
1728 gboolean add_specific;
1730 add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1732 /* Insert the account and notify the observers */
1733 insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1735 /* If it's the first remote account then add the connection
1736 specific SMTP servers as well */
1738 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1742 /* This is the callback of the tny_camel_account_set_online called in
1743 on_account_removed to disconnect the account */
1745 on_account_disconnect_when_removing (TnyCamelAccount *account,
1750 ModestTnyAccountStore *self;
1751 ModestTnyAccountStorePrivate *priv;
1753 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1754 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1756 /* Remove the connection-status-changed handler if it's still there */
1757 if (modest_signal_mgr_is_connected (priv->sighandlers,
1759 "connection_status_changed")) {
1760 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1762 "connection_status_changed");
1765 /* Cancel all pending operations */
1766 tny_account_cancel (TNY_ACCOUNT (account));
1768 /* Unref the extra reference added by get_server_account */
1769 g_object_unref (account);
1771 /* Clear the cache if it's an store account */
1772 if (TNY_IS_STORE_ACCOUNT (account)) {
1773 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1774 } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1775 ModestTnySendQueue* send_queue;
1776 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1777 if (TNY_IS_SEND_QUEUE (send_queue)) {
1778 if (modest_tny_send_queue_sending_in_progress (send_queue))
1779 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1780 TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE,
1782 modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1788 * We use this one for both removing "normal" and "connection
1789 * specific" transport accounts
1792 remove_transport_account (ModestTnyAccountStore *self,
1793 TnyTransportAccount *transport_account)
1795 ModestTnyAccountStorePrivate *priv;
1796 TnyFolder *outbox = NULL;
1798 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1800 /* Remove it from the list of accounts and notify the
1801 observers. Do not need to wait for account
1803 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1804 tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1806 /* Remove the OUTBOX of the account from the global outbox */
1807 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1809 if (TNY_IS_FOLDER (outbox)) {
1810 TnyAccount *local_account = NULL;
1811 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1813 if (outbox_account) {
1814 tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1815 /* Remove existing emails to send */
1816 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1817 g_object_unref (outbox_account);
1820 local_account = modest_tny_account_store_get_local_folders_account (self);
1821 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1824 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1826 /* Notify the change in the local account */
1827 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1828 g_object_unref (local_account);
1830 g_warning ("Removing a transport account that has no outbox");
1833 /* Cancel all pending operations */
1834 tny_account_cancel (TNY_ACCOUNT (transport_account));
1836 /* Disconnect and notify the observers. The callback will free the reference */
1837 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1838 on_account_disconnect_when_removing, self);
1842 images_cache_remove_filter (TnyStreamCache *self, const gchar *id, const gchar *account_name)
1844 gchar *account_name_with_separator;
1846 if (account_name == NULL || account_name[0] == '\0')
1849 if (id == NULL || id[0] == '\0')
1852 account_name_with_separator = g_strconcat (account_name, "__", NULL);
1854 result = (g_str_has_prefix (id, account_name));
1855 g_free (account_name_with_separator);
1861 on_account_removed (ModestAccountMgr *acc_mgr,
1862 const gchar *account,
1865 TnyAccount *store_account = NULL, *transport_account = NULL;
1866 ModestTnyAccountStore *self;
1867 ModestTnyAccountStorePrivate *priv;
1868 TnyStreamCache *stream_cache;
1870 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1871 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1873 /* Get the server and the transport account */
1875 modest_tny_account_store_get_server_account (self, account,
1876 TNY_ACCOUNT_TYPE_STORE);
1878 modest_tny_account_store_get_server_account (self, account,
1879 TNY_ACCOUNT_TYPE_TRANSPORT);
1881 /* If there was any problem creating the account, for example,
1882 with the configuration system this could not exist */
1883 if (TNY_IS_STORE_ACCOUNT(store_account)) {
1884 /* Forget any cached password for the account */
1885 forget_password_in_memory (self, tny_account_get_id (store_account));
1887 /* Remove it from the list of accounts and notify the
1888 observers. Do not need to wait for account
1890 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1891 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1893 /* Cancel all pending operations */
1894 tny_account_cancel (TNY_ACCOUNT (store_account));
1896 /* Disconnect before deleting the cache, because the
1897 disconnection will rewrite the cache to the
1899 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1900 on_account_disconnect_when_removing, self);
1902 g_warning ("%s: no store account for account %s\n",
1903 __FUNCTION__, account);
1906 /* If there was any problem creating the account, for example,
1907 with the configuration system this could not exist */
1908 if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1910 /* Forget any cached password for the account */
1911 forget_password_in_memory (self, tny_account_get_id (transport_account));
1913 /* Remove transport account. It'll free the reference
1914 added by get_server_account */
1915 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1917 g_warning ("%s: no transport account for account %s\n",
1918 __FUNCTION__, account);
1921 /* Remove cached images */
1922 stream_cache = modest_runtime_get_images_cache ();
1923 tny_stream_cache_remove (stream_cache, (TnyStreamCacheRemoveFilter) images_cache_remove_filter, (gpointer) account);
1925 /* If there are no more user accounts then delete the
1926 transport specific SMTP servers */
1927 if (only_local_accounts (self))
1928 remove_connection_specific_transport_accounts (self);
1931 TnyTransportAccount *
1932 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1935 ModestTnyAccountStorePrivate *priv = NULL;
1936 TnyAccount * tny_account = NULL;
1938 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1940 /* Add the account: */
1942 modest_tny_account_new_from_server_account_name (priv->account_mgr,
1948 g_object_set_data (G_OBJECT(tny_account),
1951 g_object_set_data (G_OBJECT(tny_account),
1952 "connection_specific",
1953 GINT_TO_POINTER (TRUE));
1955 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1956 add_outbox_from_transport_account_to_global_outbox (self,
1961 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1964 return TNY_TRANSPORT_ACCOUNT (tny_account);
1968 foreach_free_string(gpointer data,
1975 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1977 ModestTnyAccountStorePrivate *priv = NULL;
1978 GSList *list_specifics = NULL, *iter = NULL;
1981 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1983 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
1984 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1985 MODEST_CONF_VALUE_STRING, &err);
1988 g_return_if_reached ();
1992 /* Look at each connection-specific transport account for the
1993 * modest account: */
1994 iter = list_specifics;
1996 /* The list alternates between the connection name and the transport name: */
1997 iter = g_slist_next (iter);
1999 const gchar* transport_account_name = (const gchar*) (iter->data);
2000 TnyTransportAccount * account = NULL;
2001 account = modest_tny_account_store_new_connection_specific_transport_account (
2002 self, transport_account_name);
2004 g_object_unref (account);
2006 iter = g_slist_next (iter);
2010 g_slist_foreach (list_specifics, foreach_free_string, NULL);
2011 g_slist_free (list_specifics);
2015 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2017 ModestTnyAccountStorePrivate *priv = NULL;
2018 GSList *list_specifics = NULL, *iter = NULL;
2021 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2024 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2025 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2026 MODEST_CONF_VALUE_STRING, &err);
2029 g_return_if_reached ();
2033 /* Look at each connection-specific transport account for the
2034 * modest account: */
2035 iter = list_specifics;
2037 /* The list alternates between the connection name and the transport name: */
2038 iter = g_slist_next (iter);
2040 const gchar* transport_account_name = (const gchar*) (iter->data);
2041 TnyAccount * account;
2042 account = modest_tny_account_store_get_server_account (self,
2043 transport_account_name,
2044 TNY_ACCOUNT_TYPE_TRANSPORT);
2046 /* the call will free the reference */
2048 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2050 iter = g_slist_next (iter);
2054 g_slist_foreach (list_specifics, foreach_free_string, NULL);
2055 g_slist_free (list_specifics);
2060 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self,
2062 TnyAccount **ac_out)
2064 TnyIterator *acc_iter;
2065 ModestTnyAccountStorePrivate *priv;
2067 TnyAccount *msg_account = NULL;
2069 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2070 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2072 acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2073 while (!msg && !tny_iterator_is_done (acc_iter)) {
2074 TnyList *folders = tny_simple_list_new ();
2075 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2076 TnyIterator *folders_iter = NULL;
2078 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, FALSE, NULL);
2079 folders_iter = tny_list_create_iterator (folders);
2081 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2082 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2083 msg = tny_folder_find_msg (folder, uri, NULL);
2086 msg_account = g_object_ref (account);
2088 g_object_unref (folder);
2089 tny_iterator_next (folders_iter);
2091 g_object_unref (folders_iter);
2093 g_object_unref (folders);
2094 g_object_unref (account);
2095 tny_iterator_next (acc_iter);
2098 g_object_unref (acc_iter);
2101 *ac_out = msg_account;
2106 TnyTransportAccount *
2107 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2109 TnyIterator *acc_iter;
2110 ModestTnyAccountStorePrivate *priv;
2111 TnyTransportAccount *header_acc = NULL;
2114 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2115 g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2116 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2117 msg_id = modest_tny_send_queue_get_msg_id (header);
2118 acc_iter = tny_list_create_iterator (priv->transport_accounts);
2119 while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2120 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2121 ModestTnySendQueue *send_queue;
2122 ModestTnySendQueueStatus status;
2123 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2124 if (TNY_IS_SEND_QUEUE (send_queue)) {
2125 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2126 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2127 header_acc = g_object_ref(account);
2129 g_object_unref (account);
2130 tny_iterator_next (acc_iter);
2132 g_object_unref(acc_iter);
2140 ModestTnyAccountStore *account_store;
2141 ModestTnyAccountStoreShutdownCallback callback;
2147 account_shutdown_callback (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer userdata)
2149 ShutdownOpData *op_data = (ShutdownOpData *) userdata;
2151 if (op_data->pending == 0) {
2152 if (op_data->callback)
2153 op_data->callback (op_data->account_store, op_data->userdata);
2154 g_object_unref (op_data->account_store);
2157 g_object_unref (op_data->account_store);
2162 account_shutdown (TnyAccount *account, ShutdownOpData *op_data)
2164 g_return_if_fail (account && TNY_IS_ACCOUNT(account));
2166 if (TNY_IS_STORE_ACCOUNT (account) &&
2167 !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
2170 /* Disconnect account */
2171 if (tny_account_get_connection_status (account) == TNY_CONNECTION_STATUS_CONNECTED) {
2172 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE,
2173 account_shutdown_callback, op_data);
2179 g_object_unref (op_data->account_store);
2184 modest_tny_account_store_shutdown (ModestTnyAccountStore *self,
2185 ModestTnyAccountStoreShutdownCallback callback,
2188 gint i, num_accounts;
2189 ShutdownOpData *op_data;
2190 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2192 /* Get references */
2193 num_accounts = tny_list_get_length (priv->store_accounts) +
2194 tny_list_get_length (priv->transport_accounts);
2195 for (i = 0 ; i < num_accounts ; i++)
2196 g_object_ref (self);
2198 /* Create the helper object */
2199 op_data = g_new0 (ShutdownOpData, 1);
2200 op_data->callback = callback;
2201 op_data->userdata = userdata;
2202 op_data->pending = num_accounts;
2203 op_data->account_store = self;
2205 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
2206 if (priv->store_accounts) {
2207 tny_list_foreach (priv->store_accounts, (GFunc)account_shutdown, op_data);
2210 if (priv->transport_accounts) {
2211 tny_list_foreach (priv->transport_accounts, (GFunc)account_shutdown, op_data);
2214 if (op_data->pending == 0) {
2215 if (op_data->callback)
2216 op_data->callback (op_data->account_store, op_data->userdata);
2222 modest_tny_account_store_is_send_mail_blocked (ModestTnyAccountStore *self)
2224 ModestTnyAccountStorePrivate *priv;
2226 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2228 return priv->send_mail_blocked;
2232 modest_tny_account_store_set_send_mail_blocked (ModestTnyAccountStore *self,
2235 ModestTnyAccountStorePrivate *priv;
2237 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2239 priv->send_mail_blocked = blocked;
2243 count_remote_accounts (gpointer data, gpointer user_data)
2245 TnyFolderStore *account = TNY_FOLDER_STORE (data);
2246 gint *count = (gint *) user_data;
2248 if (modest_tny_folder_store_is_remote (account))
2253 modest_tny_account_store_get_num_remote_accounts (ModestTnyAccountStore *self)
2255 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2258 /* Count remote accounts */
2259 tny_list_foreach (priv->store_accounts, (GFunc) count_remote_accounts, &count);