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>
44 #include <modest-runtime.h>
45 #include <modest-marshal.h>
46 #include <modest-protocol-info.h>
47 #include <modest-local-folder-info.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-local-folders-account.h>
50 #include <modest-account-mgr.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <widgets/modest-window-mgr.h>
53 #include <modest-signal-mgr.h>
54 #include <modest-debug.h>
56 #include "modest-tny-account-store.h"
57 #include "modest-tny-platform-factory.h"
58 #include <tny-gtk-lockable.h>
59 #include <camel/camel.h>
60 #include <modest-platform.h>
61 #include "modest-ui-actions.h"
62 #include <widgets/modest-account-settings-dialog.h>
64 #ifdef MODEST_PLATFORM_MAEMO
65 #include <tny-maemo-conic-device.h>
66 #include <maemo/modest-maemo-utils.h>
69 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
71 /* 'private'/'protected' functions */
72 static void modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass);
73 static void modest_tny_account_store_finalize (GObject *obj);
74 static void modest_tny_account_store_instance_init (ModestTnyAccountStore *obj);
75 static void modest_tny_account_store_init (gpointer g, gpointer iface_data);
76 static void modest_tny_account_store_base_init (gpointer g_class);
78 static void on_account_inserted (ModestAccountMgr *acc_mgr,
82 static void add_existing_accounts (ModestTnyAccountStore *self);
84 static void insert_account (ModestTnyAccountStore *self,
88 static void on_account_removed (ModestAccountMgr *acc_mgr,
92 static gchar* get_password (TnyAccount *account,
93 const gchar * prompt_not_used,
96 static void forget_password (TnyAccount *account);
98 static void on_vfs_volume_mounted (GnomeVFSVolumeMonitor *volume_monitor,
99 GnomeVFSVolume *volume,
102 static void on_vfs_volume_unmounted (GnomeVFSVolumeMonitor *volume_monitor,
103 GnomeVFSVolume *volume,
106 static void forget_password_in_memory (ModestTnyAccountStore *self,
107 const gchar *server_account_name);
109 static void add_connection_specific_transport_accounts (ModestTnyAccountStore *self);
111 static void remove_connection_specific_transport_accounts (ModestTnyAccountStore *self);
113 static void connection_status_changed (TnyAccount *account,
114 TnyConnectionStatus status,
117 static gboolean only_local_accounts (ModestTnyAccountStore *self);
119 /* list my signals */
121 ACCOUNT_CHANGED_SIGNAL,
122 ACCOUNT_INSERTED_SIGNAL,
123 ACCOUNT_REMOVED_SIGNAL,
125 PASSWORD_REQUESTED_SIGNAL,
129 typedef struct _ModestTnyAccountStorePrivate ModestTnyAccountStorePrivate;
130 struct _ModestTnyAccountStorePrivate {
132 GHashTable *password_hash;
133 GHashTable *account_settings_dialog_hash;
134 ModestAccountMgr *account_mgr;
135 TnySessionCamel *session;
140 /* We cache the lists of accounts here */
141 TnyList *store_accounts;
142 TnyList *transport_accounts;
143 TnyList *store_accounts_outboxes;
145 /* Matches transport accounts and outbox folder */
146 GHashTable *outbox_of_transport;
149 #define MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
150 MODEST_TYPE_TNY_ACCOUNT_STORE, \
151 ModestTnyAccountStorePrivate))
154 static GObjectClass *parent_class = NULL;
156 static guint signals[LAST_SIGNAL] = {0};
159 modest_tny_account_store_get_type (void)
161 static GType my_type = 0;
164 static const GTypeInfo my_info = {
165 sizeof(ModestTnyAccountStoreClass),
166 modest_tny_account_store_base_init, /* base init */
167 NULL, /* base finalize */
168 (GClassInitFunc) modest_tny_account_store_class_init,
169 NULL, /* class finalize */
170 NULL, /* class data */
171 sizeof(ModestTnyAccountStore),
173 (GInstanceInitFunc) modest_tny_account_store_instance_init,
177 static const GInterfaceInfo iface_info = {
178 (GInterfaceInitFunc) modest_tny_account_store_init,
179 NULL, /* interface_finalize */
180 NULL /* interface_data */
183 my_type = g_type_register_static (G_TYPE_OBJECT,
184 "ModestTnyAccountStore",
186 g_type_add_interface_static (my_type, TNY_TYPE_ACCOUNT_STORE,
194 modest_tny_account_store_base_init (gpointer g_class)
196 static gboolean tny_account_store_initialized = FALSE;
198 if (!tny_account_store_initialized) {
200 signals[ACCOUNT_CHANGED_SIGNAL] =
201 g_signal_new ("account_changed",
202 MODEST_TYPE_TNY_ACCOUNT_STORE,
204 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_changed),
206 g_cclosure_marshal_VOID__OBJECT,
207 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
209 signals[ACCOUNT_INSERTED_SIGNAL] =
210 g_signal_new ("account_inserted",
211 MODEST_TYPE_TNY_ACCOUNT_STORE,
213 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_inserted),
215 g_cclosure_marshal_VOID__OBJECT,
216 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
218 signals[ACCOUNT_REMOVED_SIGNAL] =
219 g_signal_new ("account_removed",
220 MODEST_TYPE_TNY_ACCOUNT_STORE,
222 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_removed),
224 g_cclosure_marshal_VOID__OBJECT,
225 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
227 signals[PASSWORD_REQUESTED_SIGNAL] =
228 g_signal_new ("password_requested",
229 MODEST_TYPE_TNY_ACCOUNT_STORE,
231 G_STRUCT_OFFSET(ModestTnyAccountStoreClass, password_requested),
233 modest_marshal_VOID__STRING_POINTER_POINTER_POINTER_POINTER,
234 G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
237 tny_account_store_initialized = TRUE;
243 modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass)
245 GObjectClass *gobject_class;
246 gobject_class = (GObjectClass*) klass;
248 parent_class = g_type_class_peek_parent (klass);
249 gobject_class->finalize = modest_tny_account_store_finalize;
251 g_type_class_add_private (gobject_class,
252 sizeof(ModestTnyAccountStorePrivate));
256 modest_tny_account_store_instance_init (ModestTnyAccountStore *obj)
258 GnomeVFSVolumeMonitor* monitor = NULL;
259 ModestTnyAccountStorePrivate *priv;
261 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
263 priv->cache_dir = NULL;
264 priv->account_mgr = NULL;
265 priv->session = NULL;
267 priv->sighandlers = NULL;
269 priv->outbox_of_transport = g_hash_table_new_full (g_direct_hash,
274 /* An in-memory store of passwords,
275 * for passwords that are not remembered in the configuration,
276 * so they need to be asked for from the user once in each session:
278 priv->password_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
281 /* A hash-map of modest account names to dialog pointers,
282 * so we can avoid showing the account settings twice for the same modest account: */
283 priv->account_settings_dialog_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 MODEST_MCC1_VOLUMEPATH);
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;
364 self = MODEST_TNY_ACCOUNT_STORE(user_data);
365 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
367 /* Check whether this was the external MMC1 card: */
368 uri = gnome_vfs_volume_get_activation_uri (volume);
370 if (uri && (!strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI))) {
371 add_mmc_account (self, TRUE /* emit the insert signal. */);
378 on_vfs_volume_unmounted(GnomeVFSVolumeMonitor *volume_monitor,
379 GnomeVFSVolume *volume,
382 ModestTnyAccountStore *self;
383 ModestTnyAccountStorePrivate *priv;
386 self = MODEST_TNY_ACCOUNT_STORE(user_data);
387 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
389 /* Check whether this was the external MMC1 card: */
390 uri = gnome_vfs_volume_get_activation_uri (volume);
391 if (uri && (strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI) == 0)) {
392 TnyAccount *mmc_account = NULL;
393 gboolean found = FALSE;
394 TnyIterator *iter = NULL;
396 iter = tny_list_create_iterator (priv->store_accounts);
397 while (!tny_iterator_is_done (iter) && !found) {
400 account = TNY_ACCOUNT (tny_iterator_get_current (iter));
401 if (modest_tny_account_is_memory_card_account (account)) {
403 mmc_account = g_object_ref (account);
405 g_object_unref (account);
406 tny_iterator_next (iter);
408 g_object_unref (iter);
411 /* Remove from the list */
412 tny_list_remove (priv->store_accounts, G_OBJECT (mmc_account));
414 /* Notify observers */
415 g_signal_emit (G_OBJECT (self),
416 signals [ACCOUNT_REMOVED_SIGNAL],
419 g_object_unref (mmc_account);
421 g_warning ("%s: there was no store account for the unmounted MMC",
429 * forget_password_in_memory
430 * @self: a TnyAccountStore instance
431 * @account: A server account.
433 * Forget any password stored in memory for this account.
434 * For instance, this should be called when the user has changed the password in the account settings.
437 forget_password_in_memory (ModestTnyAccountStore *self,
438 const gchar * server_account_name)
440 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
442 if (server_account_name && priv->password_hash) {
443 g_hash_table_remove (priv->password_hash, server_account_name);
448 on_account_changed (ModestAccountMgr *acc_mgr,
449 const gchar *account_name,
450 TnyAccountType account_type,
453 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
454 ModestTnyAccountStorePrivate *priv;
455 TnyList* account_list;
456 gboolean found = FALSE;
457 TnyIterator *iter = NULL;
459 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
460 account_list = (account_type == TNY_ACCOUNT_TYPE_STORE ?
461 priv->store_accounts :
462 priv->transport_accounts);
464 iter = tny_list_create_iterator (account_list);
465 while (!tny_iterator_is_done (iter) && !found) {
466 TnyAccount *tny_account;
467 tny_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
469 if (!strcmp (tny_account_get_id (tny_account), account_name)) {
471 modest_tny_account_update_from_account (tny_account, get_password, forget_password);
472 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_CHANGED_SIGNAL], 0, tny_account);
474 g_object_unref (tny_account);
476 tny_iterator_next (iter);
480 g_object_unref (iter);
484 on_account_settings_hide (GtkWidget *widget, gpointer user_data)
486 /* This is easier than using a struct for the user_data: */
487 ModestTnyAccountStore *self = modest_runtime_get_account_store();
488 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
490 gchar *account_name = (gchar *) user_data;
492 g_hash_table_remove (priv->account_settings_dialog_hash, account_name);
496 show_password_warning_only (const gchar *msg)
498 ModestWindow *main_window =
499 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE); /* don't create */
501 /* Show an explanatory temporary banner: */
503 modest_platform_information_banner (NULL, NULL, msg);
507 show_wrong_password_dialog (TnyAccount *account)
509 /* This is easier than using a struct for the user_data: */
510 ModestTnyAccountStore *self = modest_runtime_get_account_store();
511 GtkWidget *dialog = NULL;
513 if (g_object_get_data (G_OBJECT (account), "connection_specific") != NULL) {
514 modest_ui_actions_on_smtp_servers (NULL, NULL);
516 const gchar *modest_account_name;
517 modest_account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
518 dialog = modest_tny_account_store_show_account_settings_dialog (self, modest_account_name);
520 /* Show an explanatory temporary banner: */
521 modest_platform_information_banner (dialog, NULL, _("mcen_ib_username_pw_incorrect"));
524 /* This callback will be called by Tinymail when it needs the password
525 * from the user or the account settings.
526 * It can also call forget_password() before calling this,
527 * so that we clear wrong passwords out of our account settings.
528 * Note that TnyAccount here will be the server account. */
530 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
532 ModestTnyAccountStore *self = NULL;
533 ModestTnyAccountStorePrivate *priv;
534 gchar *username = NULL;
536 gpointer pwd_ptr = NULL;
537 gboolean already_asked = FALSE;
538 const gchar *server_account_name;
541 g_return_val_if_fail (account, NULL);
544 g_debug ("%s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
547 /* Get a reference to myself */
548 self = MODEST_TNY_ACCOUNT_STORE (g_object_get_data (G_OBJECT(account), "account_store"));
549 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
551 /* Ensure that we still have this account. It could happen
552 that a set_online was requested *before* removing an
553 account, and due to tinymail emits the get_password
554 function using a g_idle the account could be actually
555 removed *before* this function was really called */
556 url_string = tny_account_get_url_string (account);
558 TnyAccount *tmp_account;
560 tmp_account = tny_account_store_find_account (TNY_ACCOUNT_STORE (self),
568 g_object_unref (tmp_account);
571 server_account_name = tny_account_get_id (account);
572 if (!server_account_name || !self) {
573 g_warning ("modest: %s: could not retrieve account_store for account %s",
574 __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
581 /* This hash map stores passwords, including passwords that are not stored in gconf. */
582 /* Is it in the hash? if it's already there, it must be wrong... */
583 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
584 * type-punned ptrs...*/
585 already_asked = priv->password_hash &&
586 g_hash_table_lookup_extended (priv->password_hash,
589 (gpointer*)&pwd_ptr);
591 g_debug ("%s: Already asked = %d\n", __FUNCTION__, already_asked);
594 /* If the password is not already there, try ModestConf */
595 if (!already_asked) {
596 pwd = modest_account_mgr_get_server_account_password (priv->account_mgr,
597 server_account_name);
598 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
601 /* If it was already asked, it must have been wrong, so ask again */
602 if (already_asked || !pwd || strlen(pwd) == 0) {
603 /* As per the UI spec, if no password was set in the account settings,
604 * ask for it now. But if the password is wrong in the account settings,
605 * then show a banner and the account settings dialog so it can be corrected:
607 ModestTransportStoreProtocol proto;
608 const gboolean settings_have_password =
609 modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
611 /* Show an error and after that ask for a password */
612 proto = modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (account));
613 if (proto == MODEST_PROTOCOL_TRANSPORT_SMTP) {
614 gchar *username = NULL, *msg = NULL;
615 username = modest_account_mgr_get_server_account_username (priv->account_mgr,
616 server_account_name);
617 if (!username || strlen(username) == 0) {
618 msg = g_strdup_printf (_("emev_ni_ui_smtp_userid_invalid"),
619 tny_account_get_name (account),
620 tny_account_get_hostname (account));
623 password = modest_account_mgr_get_server_account_password (priv->account_mgr,
624 server_account_name);
625 if (!password || strlen(password) == 0)
626 msg = g_strdup_printf (_("emev_ni_ui_smtp_passwd_invalid"),
627 tny_account_get_name (account),
628 tny_account_get_hostname (account));
630 msg = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
631 tny_account_get_hostname (account));
636 modest_platform_run_information_dialog (NULL, msg, TRUE);
643 if (settings_have_password) {
644 /* The password must be wrong, so show the account settings dialog so it can be corrected: */
645 show_wrong_password_dialog (account);
653 /* we don't have it yet. Get the password from the user */
654 const gchar* account_id = tny_account_get_id (account);
655 gboolean remember = FALSE;
660 gboolean username_known =
661 modest_account_mgr_get_server_account_username_has_succeeded(priv->account_mgr,
662 server_account_name);
663 /* If the login has ever succeeded then show a specific message */
665 msg = dgettext ("hildon-common-strings", "ecdg_ib_set_password_incorrect");
667 msg = _("mcen_ib_username_pw_incorrect");
668 show_password_warning_only (msg);
671 /* Request password */
672 g_signal_emit (G_OBJECT (self), signals[PASSWORD_REQUESTED_SIGNAL], 0,
673 account_id, /* server_account_name */
674 &username, &pwd, cancel, &remember);
678 /* The password will be returned as the result,
679 * but we need to tell tinymail about the username too: */
681 /* WARNING: I disabled setting username as this can cause locks. Anyway,
682 * as now we have the password dialog username entry always dimmed
683 * this shouldn't be a problem */
686 /* tny_account_set_user (account, username); */
688 /* Do not save the password in gconf, because
689 * the UI spec says "The password will never
690 * be saved in the account": */
692 /* We need to dup the string even knowing that
693 it's already a dup of the contents of an
694 entry, because it if it's wrong, then camel
696 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
698 g_hash_table_remove (priv->password_hash, server_account_name);
713 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
715 g_return_if_fail (account);
717 ModestTnyAccountStorePrivate *priv;
719 gpointer pwd_ptr = NULL;
720 gboolean already_asked = FALSE;
722 const gchar *server_account_name = tny_account_get_id (account);
724 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
726 /* This hash map stores passwords, including passwords that are not stored in gconf. */
727 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
728 * type-punned ptrs...*/
729 already_asked = priv->password_hash &&
730 g_hash_table_lookup_extended (priv->password_hash,
733 (gpointer*)&pwd_ptr);
736 g_hash_table_remove (priv->password_hash, server_account_name);
744 /* tinymail calls this if the connection failed due to an incorrect password.
745 * And it seems to call this for any general connection failure. */
747 forget_password (TnyAccount *account)
749 ModestTnyAccountStore *self;
750 ModestTnyAccountStorePrivate *priv;
751 const TnyAccountStore *account_store;
755 account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
757 self = MODEST_TNY_ACCOUNT_STORE (account_store);
758 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
759 key = tny_account_get_id (account);
761 /* Do not remove the key, this will allow us to detect that we
762 have already asked for it at least once */
763 pwd = g_hash_table_lookup (priv->password_hash, key);
765 memset (pwd, 0, strlen (pwd));
766 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
769 /* Remove from configuration system */
771 modest_account_mgr_unset (priv->account_mgr,
772 key, MODEST_ACCOUNT_PASSWORD, TRUE);
777 modest_tny_account_store_finalize (GObject *obj)
779 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(obj);
780 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
782 g_free (priv->cache_dir);
783 priv->cache_dir = NULL;
785 if (priv->password_hash) {
786 g_hash_table_destroy (priv->password_hash);
787 priv->password_hash = NULL;
790 if (priv->account_settings_dialog_hash) {
791 g_hash_table_destroy (priv->account_settings_dialog_hash);
792 priv->account_settings_dialog_hash = NULL;
795 if (priv->outbox_of_transport) {
796 g_hash_table_destroy (priv->outbox_of_transport);
797 priv->outbox_of_transport = NULL;
800 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
801 priv->sighandlers = NULL;
803 if (priv->account_mgr) {
804 g_object_unref (G_OBJECT(priv->account_mgr));
805 priv->account_mgr = NULL;
809 g_object_unref (G_OBJECT(priv->device));
813 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
814 if (priv->store_accounts) {
815 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
816 g_object_unref (priv->store_accounts);
817 priv->store_accounts = NULL;
820 if (priv->transport_accounts) {
821 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
822 g_object_unref (priv->transport_accounts);
823 priv->transport_accounts = NULL;
826 if (priv->store_accounts_outboxes) {
827 g_object_unref (priv->store_accounts_outboxes);
828 priv->store_accounts_outboxes = NULL;
832 camel_object_unref (CAMEL_OBJECT(priv->session));
833 priv->session = NULL;
836 G_OBJECT_CLASS(parent_class)->finalize (obj);
840 volume_path_is_mounted (const gchar* path)
842 g_return_val_if_fail (path, FALSE);
844 gboolean result = FALSE;
845 gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
846 g_return_val_if_fail (path_as_uri, FALSE);
848 /* Get the monitor singleton: */
849 GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
851 /* This seems like a simpler way to do this, but it returns a
852 * GnomeVFSVolume even if the drive is not mounted: */
854 GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor,
855 MODEST_MCC1_VOLUMEPATH);
857 gnome_vfs_volume_unref(volume);
861 /* Get the mounted volumes from the monitor: */
862 GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
864 for (iter = list; iter; iter = g_list_next (iter)) {
865 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
869 gnome_vfs_volume_get_display_name (volume);
870 printf ("volume display name=%s\n", display_name);
871 g_free (display_name);
875 gnome_vfs_volume_get_activation_uri (volume);
876 /* printf (" uri=%s\n", uri); */
877 if (uri && (strcmp (uri, path_as_uri) == 0))
882 gnome_vfs_volume_unref (volume);
888 g_free (path_as_uri);
893 ModestTnyAccountStore*
894 modest_tny_account_store_new (ModestAccountMgr *account_mgr,
898 ModestTnyAccountStorePrivate *priv;
899 TnyAccount *local_account = NULL;
900 TnyLockable *lockable;
902 g_return_val_if_fail (account_mgr, NULL);
903 g_return_val_if_fail (device, NULL);
905 obj = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
906 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
908 priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
909 priv->device = g_object_ref (device);
911 priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
912 if (!priv->session) {
913 g_warning ("failed to get TnySessionCamel");
917 /* Set the ui locker */
918 lockable = tny_gtk_lockable_new ();
919 tny_session_camel_set_ui_locker (priv->session, lockable);
920 g_object_unref (lockable);
922 /* Connect signals */
923 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
924 G_OBJECT(account_mgr), "account_inserted",
925 G_CALLBACK (on_account_inserted), obj);
926 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
927 G_OBJECT(account_mgr), "account_changed",
928 G_CALLBACK (on_account_changed), obj);
929 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
930 G_OBJECT(account_mgr), "account_removed",
931 G_CALLBACK (on_account_removed), obj);
933 /* Create the lists of accounts */
934 priv->store_accounts = tny_simple_list_new ();
935 priv->transport_accounts = tny_simple_list_new ();
936 priv->store_accounts_outboxes = tny_simple_list_new ();
938 /* Create the local folders account */
940 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
941 tny_list_append (priv->store_accounts, G_OBJECT(local_account));
942 g_object_unref (local_account);
944 /* Add the other remote accounts. Do this after adding the
945 local account, because we need to add our outboxes to the
946 global OUTBOX hosted in the local account */
947 add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
949 /* Add connection-specific transport accounts if there are any
950 accounts available */
951 if (!only_local_accounts (MODEST_TNY_ACCOUNT_STORE(obj)))
952 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
954 /* This is a singleton, so it does not need to be unrefed. */
955 if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
957 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */);
960 return MODEST_TNY_ACCOUNT_STORE(obj);
964 modest_tny_account_store_get_accounts (TnyAccountStore *self,
966 TnyGetAccountsRequestType request_type)
968 ModestTnyAccountStorePrivate *priv;
970 g_return_if_fail (self);
971 g_return_if_fail (TNY_IS_LIST(list));
973 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
975 switch (request_type) {
976 case TNY_ACCOUNT_STORE_BOTH:
977 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
978 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
980 case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
981 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
983 case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
984 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
987 g_return_if_reached ();
990 /* Initialize session. Why do we need this ??? */
991 tny_session_camel_set_initialized (priv->session);
996 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
998 ModestTnyAccountStorePrivate *priv;
999 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1001 if (!priv->cache_dir)
1002 priv->cache_dir = g_build_filename (g_get_home_dir(),
1003 MODEST_DIR, MODEST_CACHE_DIR, NULL);
1004 return priv->cache_dir;
1009 * callers need to unref
1012 modest_tny_account_store_get_device (TnyAccountStore *self)
1014 ModestTnyAccountStorePrivate *priv;
1016 g_return_val_if_fail (self, NULL);
1018 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1021 return g_object_ref (G_OBJECT(priv->device));
1028 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1030 return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self),
1031 MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1038 modest_tny_account_store_alert (TnyAccountStore *self,
1039 TnyAccount *account,
1044 ModestTransportStoreProtocol proto =
1045 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
1046 const gchar* server_name = "";
1047 gchar *prompt = NULL;
1048 gboolean retval = TRUE;
1050 /* NOTE: account may be NULL in some cases */
1051 g_return_val_if_fail (error, FALSE);
1053 /* Get the server name: */
1055 server_name = tny_account_get_hostname (account);
1056 const gchar *proto_name = tny_account_get_proto (account);
1058 proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1060 g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__,
1061 tny_account_get_id (account));
1066 switch (error->code) {
1067 case TNY_SYSTEM_ERROR_CANCEL:
1068 /* Don't show waste the user's time by showing him a dialog telling
1069 * him that he has just cancelled something: */
1072 case TNY_SERVICE_ERROR_PROTOCOL:
1073 /* Like a BAD from IMAP (protocol error) */
1074 case TNY_SERVICE_ERROR_LOST_CONNECTION:
1075 /* Lost the connection with the service */
1076 case TNY_SERVICE_ERROR_UNAVAILABLE:
1077 /* You must be working online for this operation */
1078 case TNY_SERVICE_ERROR_CONNECT:
1080 case MODEST_PROTOCOL_STORE_POP:
1081 prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1084 case MODEST_PROTOCOL_STORE_IMAP:
1085 prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1088 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1089 prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1093 g_return_val_if_reached (FALSE);
1097 case TNY_SERVICE_ERROR_AUTHENTICATE:
1098 /* It seems that there's no better error to show with
1099 * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1100 * may appear if there's a timeout during auth */
1102 case MODEST_PROTOCOL_STORE_POP:
1103 prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1106 case MODEST_PROTOCOL_STORE_IMAP:
1107 prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1110 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1111 prompt = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
1115 g_return_val_if_reached (FALSE);
1119 case TNY_SERVICE_ERROR_CERTIFICATE:
1120 /* We'll show the proper dialog later */
1123 case TNY_SYSTEM_ERROR_MEMORY:
1124 /* Can't allocate memory for this operation */
1126 case TNY_SERVICE_ERROR_UNKNOWN:
1129 g_debug ("Unexpected error %d", error->code);
1130 g_return_val_if_reached (FALSE);
1134 if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1135 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1137 else if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE) {
1138 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1140 /* Show the account dialog if it was wrong */
1141 if (error->code == TNY_SERVICE_ERROR_CONNECT ||
1142 error->code == TNY_SERVICE_ERROR_AUTHENTICATE)
1143 show_wrong_password_dialog (account);
1148 g_debug ("%s: error code %d (%s", __FUNCTION__, error->code, error->message);
1158 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1160 TnyAccountStoreIface *klass;
1162 g_return_if_fail (g);
1164 klass = (TnyAccountStoreIface *)g;
1166 klass->get_accounts =
1167 modest_tny_account_store_get_accounts;
1168 klass->get_cache_dir =
1169 modest_tny_account_store_get_cache_dir;
1171 modest_tny_account_store_get_device;
1173 modest_tny_account_store_alert;
1174 klass->find_account =
1175 modest_tny_account_store_find_account_by_url;
1179 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1180 ModestTnyGetPassFunc func)
1182 /* not implemented, we use signals */
1183 g_printerr ("modest: set_get_pass_func not implemented\n");
1187 modest_tny_account_store_get_session (TnyAccountStore *self)
1189 g_return_val_if_fail (self, NULL);
1190 return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1194 get_tny_account_by (TnyList *accounts,
1195 ModestTnyAccountStoreQueryType type,
1198 TnyIterator *iter = NULL;
1199 gboolean found = FALSE;
1200 TnyAccount *retval = NULL;
1202 g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1204 if (tny_list_get_length(accounts) == 0) {
1205 g_warning ("%s: account list is empty", __FUNCTION__);
1209 iter = tny_list_create_iterator (accounts);
1210 while (!tny_iterator_is_done (iter) && !found) {
1211 TnyAccount *tmp_account = NULL;
1212 const gchar *val = NULL;
1214 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1215 if (!TNY_IS_ACCOUNT(tmp_account)) {
1216 g_warning ("%s: not a valid account", __FUNCTION__);
1222 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1223 val = tny_account_get_id (tmp_account);
1225 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1226 val = tny_account_get_url_string (tmp_account);
1230 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL &&
1231 tny_account_matches_url_string (tmp_account, str)) {
1232 retval = g_object_ref (tmp_account);
1235 if (val && str && strcmp (val, str) == 0) {
1236 retval = g_object_ref (tmp_account);
1240 g_object_unref (tmp_account);
1241 tny_iterator_next (iter);
1243 g_object_unref (iter);
1249 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self,
1250 ModestTnyAccountStoreQueryType type,
1253 TnyAccount *account = NULL;
1254 ModestTnyAccountStorePrivate *priv;
1256 g_return_val_if_fail (self, NULL);
1257 g_return_val_if_fail (str, NULL);
1259 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1261 /* Search in store accounts */
1262 account = get_tny_account_by (priv->store_accounts, type, str);
1264 /* If we already found something, no need to search the transport accounts */
1266 account = get_tny_account_by (priv->transport_accounts, type, str);
1268 /* If we already found something, no need to search the
1269 per-account outbox accounts */
1271 account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1274 /* Warn if nothing was found. This is generally unusual. */
1276 g_warning("%s: Failed to find account with %s=%s\n",
1278 (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",
1282 /* Returns a new reference to the account if found */
1288 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1289 const gchar *account_name,
1290 TnyAccountType type)
1292 ModestTnyAccountStorePrivate *priv = NULL;
1293 TnyAccount *retval = NULL;
1294 TnyList *account_list = NULL;
1295 TnyIterator *iter = NULL;
1298 g_return_val_if_fail (self, NULL);
1299 g_return_val_if_fail (account_name, NULL);
1300 g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE ||
1301 type == TNY_ACCOUNT_TYPE_TRANSPORT,
1304 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1306 account_list = (type == TNY_ACCOUNT_TYPE_STORE) ?
1307 priv->store_accounts :
1308 priv->transport_accounts;
1310 if (!account_list) {
1311 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__,
1312 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1316 /* Look for the server account */
1318 iter = tny_list_create_iterator (account_list);
1319 while (!tny_iterator_is_done (iter) && !found) {
1320 const gchar *modest_acc_name;
1321 TnyAccount *tmp_account;
1323 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1325 modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1327 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1329 retval = g_object_ref (tmp_account);
1331 /* Free and continue */
1332 g_object_unref (tmp_account);
1333 tny_iterator_next (iter);
1335 g_object_unref (iter);
1338 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1339 "Number of server accounts of this type=%d\n", __FUNCTION__,
1340 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1341 account_name, tny_list_get_length (account_list));
1344 /* Returns a new reference */
1349 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1350 ModestTnyAccountStore *self, const gchar *account_name)
1354 g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1355 g_return_val_if_fail (account_name, NULL);
1357 /* Get the current connection: */
1358 device = modest_runtime_get_device ();
1361 g_warning ("%s: could not get device", __FUNCTION__);
1365 if (!tny_device_is_online (device))
1368 #ifdef MODEST_HAVE_CONIC
1369 g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1371 TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);
1372 const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1373 /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1377 ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1381 const gchar *connection_id = con_ic_iap_get_id (connection);
1382 /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1386 /* Get the connection-specific transport acccount, if any: */
1387 ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1389 /* Check if this account has connection-specific SMTP enabled */
1390 if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1394 gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager,
1397 /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1398 if (!server_account_name) {
1399 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1402 TnyAccount* account = modest_tny_account_store_get_tny_account_by (self,
1403 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1404 server_account_name);
1406 /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1407 g_free (server_account_name);
1409 /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1410 g_object_unref (connection);
1414 return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1415 #endif /* MODEST_HAVE_CONIC */
1420 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1421 const gchar *account_name)
1423 g_return_val_if_fail (self, NULL);
1424 g_return_val_if_fail (account_name, NULL);
1426 if (!account_name || !self)
1429 /* Get the connection-specific transport acccount, if any: */
1430 /* Note: This gives us a reference: */
1431 TnyAccount *account =
1432 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1434 /* If there is no connection-specific transport account (the common case),
1435 * just get the regular transport account: */
1437 /* The special local folders don't have transport accounts. */
1438 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1441 /* Note: This gives us a reference: */
1442 account = modest_tny_account_store_get_server_account (self, account_name,
1443 TNY_ACCOUNT_TYPE_TRANSPORT);
1447 /* returns a reference. */
1452 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1454 TnyAccount *account = NULL;
1455 ModestTnyAccountStorePrivate *priv;
1459 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1461 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1464 iter = tny_list_create_iterator (priv->store_accounts);
1465 while (!tny_iterator_is_done (iter) && !found) {
1466 TnyAccount *tmp_account;
1468 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1469 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1470 account = g_object_ref (tmp_account);
1473 g_object_unref (tmp_account);
1474 tny_iterator_next (iter);
1476 g_object_unref (iter);
1478 /* Returns a new reference to the account */
1483 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1485 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1488 return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1489 MODEST_MMC_ACCOUNT_ID);
1493 /*********************************************************************************/
1495 add_existing_accounts (ModestTnyAccountStore *self)
1497 GSList *account_names = NULL, *iter = NULL;
1498 ModestTnyAccountStorePrivate *priv = NULL;
1500 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1502 /* These are account names, not server_account names */
1503 account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1505 for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1506 const gchar *account_name = (const gchar*) iter->data;
1508 /* Insert all enabled accounts without notifying */
1509 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1510 insert_account (self, account_name, FALSE);
1512 modest_account_mgr_free_account_names (account_names);
1516 connection_status_changed (TnyAccount *account,
1517 TnyConnectionStatus status,
1520 /* We do this here and not in the connection policy because we
1521 don't want to do it for every account, just for the
1522 accounts that are interactively added when modest is
1524 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1525 const gchar *account_name;
1526 ModestWindow *main_window;
1527 ModestTnyAccountStorePrivate *priv = NULL;
1529 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1531 /* Remove this handler */
1532 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1534 "connection_status_changed");
1536 /* Perform a send receive */
1537 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1538 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1539 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, TRUE, main_window);
1544 create_tny_account (ModestTnyAccountStore *self,
1546 TnyAccountType type,
1549 TnyAccount *account = NULL;
1550 ModestTnyAccountStorePrivate *priv = NULL;
1552 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1554 account = modest_tny_account_new_from_account (priv->account_mgr,
1561 /* Forget any cached password for the account, so that
1562 we use a new account if any */
1563 forget_password_in_memory (self, tny_account_get_id (account));
1565 /* Install a signal handler that will refresh the
1566 account the first time it becomes online. Do this
1567 only if we're adding a new account while the
1568 program is running (we do not want to do this
1570 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1571 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1573 "connection_status_changed",
1574 G_CALLBACK (connection_status_changed),
1577 /* Set the account store */
1578 g_object_set_data (G_OBJECT(account), "account_store", self);
1580 g_printerr ("modest: failed to create account for %s\n", name);
1586 typedef struct _AddOutboxInfo {
1587 ModestTnyAccountStore *account_store;
1588 TnyAccount *transport_account;
1592 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1598 TnyIterator *iter_folders;
1599 TnyFolder *per_account_outbox;
1600 TnyAccount *local_account = NULL;
1601 AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1602 ModestTnyAccountStorePrivate *priv = NULL;
1603 ModestTnyAccountStore *self;
1605 self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1606 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1608 /* Note that this could happen if there is not enough space
1609 available on disk, then the outbox folder could not be
1611 if (tny_list_get_length (list) != 1) {
1612 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1613 tny_list_get_length (list));
1617 iter_folders = tny_list_create_iterator (list);
1618 per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1619 g_object_unref (iter_folders);
1620 g_object_unref (list);
1622 /* Add the outbox of the new per-account-local-outbox account
1623 to the global local merged OUTBOX of the local folders
1625 local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1626 modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1627 per_account_outbox);
1628 /* Add the pair to the hash table */
1629 g_hash_table_insert (priv->outbox_of_transport,
1630 info->transport_account,
1631 per_account_outbox);
1633 /* Notify that the local account changed */
1634 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1635 g_object_unref (local_account);
1636 g_object_unref (per_account_outbox);
1639 g_object_unref (info->transport_account);
1640 g_slice_free (AddOutboxInfo, info);
1645 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1646 const gchar *account_name,
1647 TnyAccount *transport_account)
1649 TnyList *folders = NULL;
1650 TnyAccount *account_outbox = NULL;
1651 ModestTnyAccountStorePrivate *priv = NULL;
1652 AddOutboxInfo *info;
1654 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1656 /* Create per account local outbox */
1658 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr,
1662 if (!G_IS_OBJECT (account_outbox)) {
1663 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1667 tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1669 /* Get the outbox folder */
1670 folders = tny_simple_list_new ();
1671 info = g_slice_new0 (AddOutboxInfo);
1672 info->account_store = self;
1673 info->transport_account = g_object_ref (transport_account);
1674 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL,
1675 add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1676 g_object_unref (account_outbox);
1680 * This function will be used for both adding new accounts and for the
1681 * initialization. In the initialization we do not want to emit
1682 * signals so notify will be FALSE, in the case of account additions
1683 * we do want to notify the observers
1686 insert_account (ModestTnyAccountStore *self,
1687 const gchar *account,
1690 ModestTnyAccountStorePrivate *priv = NULL;
1691 TnyAccount *store_account = NULL, *transport_account = NULL;
1693 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1695 /* Get the server and the transport account */
1696 store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1697 if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1698 g_warning ("%s: failed to create store account", __FUNCTION__);
1702 transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1703 if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1704 g_warning ("%s: failed to create transport account", __FUNCTION__);
1705 g_object_unref (store_account);
1709 /* Add accounts to the lists */
1710 tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1711 tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1713 /* Create a new pseudo-account with an outbox for this
1714 transport account and add it to the global outbox
1715 in the local account */
1716 add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1718 /* Notify the observers. We do it after everything is
1721 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1722 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1726 g_object_unref (store_account);
1727 g_object_unref (transport_account);
1731 only_local_accounts (ModestTnyAccountStore *self)
1733 ModestTnyAccountStorePrivate *priv = NULL;
1734 gboolean only_local = TRUE;
1737 /* Check if this is the first remote account we add */
1738 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1739 iter = tny_list_create_iterator (priv->store_accounts);
1741 while (!tny_iterator_is_done (iter) && only_local) {
1742 TnyAccount *account = (TnyAccount *) tny_iterator_get_current (iter);
1743 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1745 g_object_unref (account);
1746 tny_iterator_next (iter);
1748 g_object_unref (iter);
1754 on_account_inserted (ModestAccountMgr *acc_mgr,
1755 const gchar *account,
1758 gboolean add_specific;
1760 add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1762 /* Insert the account and notify the observers */
1763 insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1765 /* If it's the first remote account then add the connection
1766 specific SMTP servers as well */
1768 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1772 /* This is the callback of the tny_camel_account_set_online called in
1773 on_account_removed to disconnect the account */
1775 on_account_disconnect_when_removing (TnyCamelAccount *account,
1780 ModestTnyAccountStore *self;
1781 ModestTnyAccountStorePrivate *priv;
1783 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1784 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1786 /* Remove the connection-status-changed handler if it's still there */
1787 if (modest_signal_mgr_is_connected (priv->sighandlers,
1789 "connection_status_changed")) {
1790 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1792 "connection_status_changed");
1795 /* Cancel all pending operations */
1796 tny_account_cancel (TNY_ACCOUNT (account));
1798 /* Unref the extra reference added by get_server_account */
1799 g_object_unref (account);
1801 /* Clear the cache if it's an store account */
1802 if (TNY_IS_STORE_ACCOUNT (account)) {
1803 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1804 } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1805 ModestTnySendQueue* send_queue;
1806 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1807 if (TNY_IS_SEND_QUEUE (send_queue)) {
1808 if (modest_tny_send_queue_sending_in_progress (send_queue))
1809 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1810 TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE,
1812 modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1818 * We use this one for both removing "normal" and "connection
1819 * specific" transport accounts
1822 remove_transport_account (ModestTnyAccountStore *self,
1823 TnyTransportAccount *transport_account)
1825 ModestTnyAccountStorePrivate *priv;
1826 TnyFolder *outbox = NULL;
1828 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1830 /* Remove it from the list of accounts and notify the
1831 observers. Do not need to wait for account
1833 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1834 tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1836 /* Remove the OUTBOX of the account from the global outbox */
1837 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1839 if (TNY_IS_FOLDER (outbox)) {
1840 TnyAccount *local_account = NULL;
1841 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1843 if (outbox_account) {
1844 tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1845 /* Remove existing emails to send */
1846 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1847 g_object_unref (outbox_account);
1850 local_account = modest_tny_account_store_get_local_folders_account (self);
1851 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1854 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1856 /* Notify the change in the local account */
1857 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1858 g_object_unref (local_account);
1860 g_warning ("Removing a transport account that has no outbox");
1863 /* Cancel all pending operations */
1864 tny_account_cancel (TNY_ACCOUNT (transport_account));
1866 /* Disconnect and notify the observers. The callback will free the reference */
1867 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1868 on_account_disconnect_when_removing, self);
1872 images_cache_remove_filter (TnyStreamCache *self, const gchar *id, const gchar *account_name)
1874 gchar *account_name_with_separator;
1876 if (account_name == NULL || account_name[0] == '\0')
1879 if (id == NULL || id[0] == '\0')
1882 account_name_with_separator = g_strconcat (account_name, "__", NULL);
1884 result = (g_str_has_prefix (id, account_name));
1885 g_free (account_name_with_separator);
1891 on_account_removed (ModestAccountMgr *acc_mgr,
1892 const gchar *account,
1895 TnyAccount *store_account = NULL, *transport_account = NULL;
1896 ModestTnyAccountStore *self;
1897 ModestTnyAccountStorePrivate *priv;
1898 TnyStreamCache *stream_cache;
1900 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1901 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1903 /* Get the server and the transport account */
1905 modest_tny_account_store_get_server_account (self, account,
1906 TNY_ACCOUNT_TYPE_STORE);
1908 modest_tny_account_store_get_server_account (self, account,
1909 TNY_ACCOUNT_TYPE_TRANSPORT);
1911 /* If there was any problem creating the account, for example,
1912 with the configuration system this could not exist */
1913 if (TNY_IS_STORE_ACCOUNT(store_account)) {
1914 /* Forget any cached password for the account */
1915 forget_password_in_memory (self, tny_account_get_id (store_account));
1917 /* Remove it from the list of accounts and notify the
1918 observers. Do not need to wait for account
1920 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1921 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1923 /* Cancel all pending operations */
1924 tny_account_cancel (TNY_ACCOUNT (store_account));
1926 /* Disconnect before deleting the cache, because the
1927 disconnection will rewrite the cache to the
1929 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1930 on_account_disconnect_when_removing, self);
1932 g_warning ("%s: no store account for account %s\n",
1933 __FUNCTION__, account);
1936 /* If there was any problem creating the account, for example,
1937 with the configuration system this could not exist */
1938 if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1940 /* Forget any cached password for the account */
1941 forget_password_in_memory (self, tny_account_get_id (transport_account));
1943 /* Remove transport account. It'll free the reference
1944 added by get_server_account */
1945 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1947 g_warning ("%s: no transport account for account %s\n",
1948 __FUNCTION__, account);
1951 /* Remove cached images */
1952 stream_cache = modest_runtime_get_images_cache ();
1953 tny_stream_cache_remove (stream_cache, (TnyStreamCacheRemoveFilter) images_cache_remove_filter, (gpointer) account);
1955 /* If there are no more user accounts then delete the
1956 transport specific SMTP servers */
1957 if (only_local_accounts (self))
1958 remove_connection_specific_transport_accounts (self);
1961 TnyTransportAccount *
1962 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1965 ModestTnyAccountStorePrivate *priv = NULL;
1966 TnyAccount * tny_account = NULL;
1968 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1970 /* Add the account: */
1972 modest_tny_account_new_from_server_account_name (priv->account_mgr,
1978 g_object_set_data (G_OBJECT(tny_account),
1981 g_object_set_data (G_OBJECT(tny_account),
1982 "connection_specific",
1983 GINT_TO_POINTER (TRUE));
1985 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1986 add_outbox_from_transport_account_to_global_outbox (self,
1991 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1994 return TNY_TRANSPORT_ACCOUNT (tny_account);
1999 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2001 ModestTnyAccountStorePrivate *priv = NULL;
2002 GSList *list_specifics = NULL, *iter = NULL;
2005 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2007 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2008 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2009 MODEST_CONF_VALUE_STRING, &err);
2012 g_return_if_reached ();
2015 /* Look at each connection-specific transport account for the
2016 * modest account: */
2017 iter = list_specifics;
2019 /* The list alternates between the connection name and the transport name: */
2020 iter = g_slist_next (iter);
2022 const gchar* transport_account_name = (const gchar*) (iter->data);
2023 TnyTransportAccount * account = NULL;
2024 account = modest_tny_account_store_new_connection_specific_transport_account (
2025 self, transport_account_name);
2027 g_object_unref (account);
2029 iter = g_slist_next (iter);
2034 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2036 ModestTnyAccountStorePrivate *priv = NULL;
2037 GSList *list_specifics = NULL, *iter = NULL;
2040 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2043 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2044 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2045 MODEST_CONF_VALUE_STRING, &err);
2048 g_return_if_reached ();
2051 /* Look at each connection-specific transport account for the
2052 * modest account: */
2053 iter = list_specifics;
2055 /* The list alternates between the connection name and the transport name: */
2056 iter = g_slist_next (iter);
2058 const gchar* transport_account_name = (const gchar*) (iter->data);
2059 TnyAccount * account;
2060 account = modest_tny_account_store_get_server_account (self,
2061 transport_account_name,
2062 TNY_ACCOUNT_TYPE_TRANSPORT);
2064 /* the call will free the reference */
2066 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2068 iter = g_slist_next (iter);
2074 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self,
2076 TnyAccount **ac_out)
2078 TnyIterator *acc_iter;
2079 ModestTnyAccountStorePrivate *priv;
2081 TnyAccount *msg_account = NULL;
2083 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2084 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2086 acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2087 while (!msg && !tny_iterator_is_done (acc_iter)) {
2088 TnyList *folders = tny_simple_list_new ();
2089 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2090 TnyIterator *folders_iter = NULL;
2092 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
2093 folders_iter = tny_list_create_iterator (folders);
2095 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2096 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2097 msg = tny_folder_find_msg (folder, uri, NULL);
2100 msg_account = g_object_ref (account);
2102 g_object_unref (folder);
2103 tny_iterator_next (folders_iter);
2105 g_object_unref (folders_iter);
2107 g_object_unref (folders);
2108 g_object_unref (account);
2109 tny_iterator_next (acc_iter);
2112 g_object_unref (acc_iter);
2115 *ac_out = msg_account;
2120 TnyTransportAccount *
2121 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2123 TnyIterator *acc_iter;
2124 ModestTnyAccountStorePrivate *priv;
2125 TnyTransportAccount *header_acc = NULL;
2128 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2129 g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2130 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2131 msg_id = modest_tny_send_queue_get_msg_id (header);
2132 acc_iter = tny_list_create_iterator (priv->transport_accounts);
2133 while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2134 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2135 ModestTnySendQueue *send_queue;
2136 ModestTnySendQueueStatus status;
2137 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2138 if (TNY_IS_SEND_QUEUE (send_queue)) {
2139 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2140 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2141 header_acc = g_object_ref(account);
2143 g_object_unref (account);
2144 tny_iterator_next (acc_iter);
2146 g_object_unref(acc_iter);
2154 modest_tny_account_store_show_account_settings_dialog (ModestTnyAccountStore *self,
2155 const gchar *account_name)
2157 ModestTnyAccountStorePrivate *priv;
2158 gpointer dialog_as_gpointer = NULL;
2161 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2162 found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
2163 account_name, NULL, (gpointer*)&dialog_as_gpointer);
2166 modest_account_settings_dialog_check_allow_changes ((ModestAccountSettingsDialog *) dialog_as_gpointer);
2167 return (GtkWidget *) dialog_as_gpointer;
2169 ModestAccountSettings *settings;
2171 dialog = (GtkWidget *) modest_account_settings_dialog_new ();
2172 settings = modest_account_mgr_load_account_settings (priv->account_mgr, account_name);
2173 modest_account_settings_dialog_set_account (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog), settings);
2174 g_object_unref (settings);
2175 modest_account_settings_dialog_switch_to_user_info (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2176 modest_account_settings_dialog_check_allow_changes (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2177 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
2179 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (account_name), dialog);
2181 g_signal_connect (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide),
2182 g_strdup (account_name));
2184 /* Show it and delete it when it closes: */
2185 g_signal_connect_swapped (dialog,
2187 G_CALLBACK (gtk_widget_destroy),
2189 gtk_widget_show (GTK_WIDGET (dialog));
2197 ModestTnyAccountStore *account_store;
2198 ModestTnyAccountStoreShutdownCallback callback;
2204 account_shutdown_callback (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer userdata)
2206 ShutdownOpData *op_data = (ShutdownOpData *) userdata;
2208 if (op_data->pending == 0) {
2209 if (op_data->callback)
2210 op_data->callback (op_data->account_store, op_data->userdata);
2211 g_object_unref (op_data->account_store);
2217 account_shutdown (TnyAccount *account, ShutdownOpData *op_data)
2219 g_return_if_fail (account && TNY_IS_ACCOUNT(account));
2221 if (TNY_IS_STORE_ACCOUNT (account) &&
2222 !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
2227 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, account_shutdown_callback, op_data);
2232 modest_tny_account_store_shutdown (ModestTnyAccountStore *self,
2233 ModestTnyAccountStoreShutdownCallback callback,
2236 ShutdownOpData *op_data = g_new0 (ShutdownOpData, 1);
2237 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2238 op_data->callback = callback;
2239 op_data->userdata = userdata;
2240 op_data->pending = 0;
2241 op_data->account_store = g_object_ref (self);
2243 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
2244 if (priv->store_accounts) {
2245 tny_list_foreach (priv->store_accounts, (GFunc)account_shutdown, op_data);
2248 if (priv->transport_accounts) {
2249 tny_list_foreach (priv->transport_accounts, (GFunc)account_shutdown, op_data);
2252 if (op_data->pending == 0) {
2253 if (op_data->callback)
2254 op_data->callback (op_data->account_store, op_data->userdata);
2255 g_object_unref (op_data->account_store);