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-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 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 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 show_password_warning_only (const gchar *msg)
486 ModestWindow *main_window =
487 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE); /* don't create */
489 /* Show an explanatory temporary banner: */
491 modest_platform_information_banner (NULL, NULL, msg);
495 show_wrong_password_dialog (TnyAccount *account)
497 if (g_object_get_data (G_OBJECT (account), "connection_specific") != NULL) {
498 modest_ui_actions_on_smtp_servers (NULL, NULL);
500 ModestAccountProtocol *proto;
501 ModestProtocolType proto_type;
502 const gchar *modest_account_name;
503 modest_account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
506 proto_type = modest_account_mgr_get_store_protocol (modest_runtime_get_account_mgr (),
507 modest_account_name);
508 proto = (ModestAccountProtocol *)
509 modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
512 /* Create and show the dialog */
513 if (proto && MODEST_IS_ACCOUNT_PROTOCOL (proto)) {
514 ModestAccountSettingsDialog *dialog =
515 modest_account_protocol_get_account_settings_dialog (proto, modest_account_name);
516 gtk_widget_show (GTK_WIDGET (dialog));
519 /* Show an explanatory temporary banner: */
520 modest_platform_information_banner (NULL, NULL, _("mcen_ib_username_pw_incorrect"));
523 /* This callback will be called by Tinymail when it needs the password
524 * from the user or the account settings.
525 * It can also call forget_password() before calling this,
526 * so that we clear wrong passwords out of our account settings.
527 * Note that TnyAccount here will be the server account. */
529 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
531 ModestTnyAccountStore *self = NULL;
532 ModestTnyAccountStorePrivate *priv;
533 gchar *username = NULL;
535 gpointer pwd_ptr = NULL;
536 gboolean already_asked = FALSE;
537 const gchar *server_account_name;
540 g_return_val_if_fail (account, NULL);
543 g_debug ("%s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
546 /* Get a reference to myself */
547 self = MODEST_TNY_ACCOUNT_STORE (g_object_get_data (G_OBJECT(account), "account_store"));
548 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
550 /* Ensure that we still have this account. It could happen
551 that a set_online was requested *before* removing an
552 account, and due to tinymail emits the get_password
553 function using a g_idle the account could be actually
554 removed *before* this function was really called */
555 url_string = tny_account_get_url_string (account);
557 TnyAccount *tmp_account;
559 tmp_account = tny_account_store_find_account (TNY_ACCOUNT_STORE (self),
567 g_object_unref (tmp_account);
570 server_account_name = tny_account_get_id (account);
571 if (!server_account_name || !self) {
572 g_warning ("modest: %s: could not retrieve account_store for account %s",
573 __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
580 /* This hash map stores passwords, including passwords that are not stored in gconf. */
581 /* Is it in the hash? if it's already there, it must be wrong... */
582 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
583 * type-punned ptrs...*/
584 already_asked = priv->password_hash &&
585 g_hash_table_lookup_extended (priv->password_hash,
588 (gpointer*)&pwd_ptr);
590 g_debug ("%s: Already asked = %d\n", __FUNCTION__, already_asked);
593 /* If the password is not already there, try ModestConf */
594 if (!already_asked) {
595 pwd = modest_account_mgr_get_server_account_password (priv->account_mgr,
596 server_account_name);
597 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
600 /* If it was already asked, it must have been wrong, so ask again */
601 if (already_asked || !pwd || strlen(pwd) == 0) {
602 gboolean settings_have_password;
603 ModestProtocolType protocol_type;
605 /* As per the UI spec, if no password was set in the account settings,
606 * ask for it now. But if the password is wrong in the account settings,
607 * then show a banner and the account settings dialog so it can be corrected:
609 settings_have_password =
610 modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
612 protocol_type = modest_tny_account_get_protocol_type (account);
614 /* Show an error and after that ask for a password */
615 if (modest_protocol_registry_protocol_type_has_tag(modest_runtime_get_protocol_registry (),
616 protocol_type, MODEST_PROTOCOL_REGISTRY_TRANSPORT_PROTOCOLS)) {
617 gchar *username = NULL, *msg = NULL;
618 username = modest_account_mgr_get_server_account_username (priv->account_mgr,
619 server_account_name);
620 if (!username || strlen(username) == 0) {
621 msg = g_strdup_printf (_("emev_ni_ui_smtp_userid_invalid"),
622 tny_account_get_name (account),
623 tny_account_get_hostname (account));
626 password = modest_account_mgr_get_server_account_password (priv->account_mgr,
627 server_account_name);
628 if (!password || strlen(password) == 0)
629 msg = g_strdup_printf (_("emev_ni_ui_smtp_passwd_invalid"),
630 tny_account_get_name (account),
631 tny_account_get_hostname (account));
633 msg = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
634 tny_account_get_hostname (account));
639 modest_platform_run_information_dialog (NULL, msg, TRUE);
646 if (settings_have_password) {
647 /* The password must be wrong, so show the account settings dialog so it can be corrected: */
648 show_wrong_password_dialog (account);
656 /* we don't have it yet. Get the password from the user */
657 const gchar* account_id = tny_account_get_id (account);
658 gboolean remember = FALSE;
663 gboolean username_known =
664 modest_account_mgr_get_server_account_username_has_succeeded(priv->account_mgr,
665 server_account_name);
666 /* If the login has ever succeeded then show a specific message */
668 msg = dgettext ("hildon-common-strings", "ecdg_ib_set_password_incorrect");
670 msg = _("mcen_ib_username_pw_incorrect");
671 show_password_warning_only (msg);
674 /* Request password */
675 g_signal_emit (G_OBJECT (self), signals[PASSWORD_REQUESTED_SIGNAL], 0,
676 account_id, /* server_account_name */
677 &username, &pwd, cancel, &remember);
681 /* The password will be returned as the result,
682 * but we need to tell tinymail about the username too: */
684 /* WARNING: I disabled setting username as this can cause locks. Anyway,
685 * as now we have the password dialog username entry always dimmed
686 * this shouldn't be a problem */
689 /* tny_account_set_user (account, username); */
691 /* Do not save the password in gconf, because
692 * the UI spec says "The password will never
693 * be saved in the account": */
695 /* We need to dup the string even knowing that
696 it's already a dup of the contents of an
697 entry, because it if it's wrong, then camel
699 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
701 g_hash_table_remove (priv->password_hash, server_account_name);
716 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
718 g_return_if_fail (account);
720 ModestTnyAccountStorePrivate *priv;
722 gpointer pwd_ptr = NULL;
723 gboolean already_asked = FALSE;
725 const gchar *server_account_name = tny_account_get_id (account);
727 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
729 /* This hash map stores passwords, including passwords that are not stored in gconf. */
730 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
731 * type-punned ptrs...*/
732 already_asked = priv->password_hash &&
733 g_hash_table_lookup_extended (priv->password_hash,
736 (gpointer*)&pwd_ptr);
739 g_hash_table_remove (priv->password_hash, server_account_name);
747 /* tinymail calls this if the connection failed due to an incorrect password.
748 * And it seems to call this for any general connection failure. */
750 forget_password (TnyAccount *account)
752 ModestTnyAccountStore *self;
753 ModestTnyAccountStorePrivate *priv;
754 const TnyAccountStore *account_store;
758 account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
760 self = MODEST_TNY_ACCOUNT_STORE (account_store);
761 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
762 key = tny_account_get_id (account);
764 /* Do not remove the key, this will allow us to detect that we
765 have already asked for it at least once */
766 pwd = g_hash_table_lookup (priv->password_hash, key);
768 memset (pwd, 0, strlen (pwd));
769 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
772 /* Remove from configuration system */
774 modest_account_mgr_unset (priv->account_mgr,
775 key, MODEST_ACCOUNT_PASSWORD, TRUE);
780 modest_tny_account_store_finalize (GObject *obj)
782 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(obj);
783 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
785 g_free (priv->cache_dir);
786 priv->cache_dir = NULL;
788 if (priv->password_hash) {
789 g_hash_table_destroy (priv->password_hash);
790 priv->password_hash = NULL;
793 if (priv->outbox_of_transport) {
794 g_hash_table_destroy (priv->outbox_of_transport);
795 priv->outbox_of_transport = NULL;
798 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
799 priv->sighandlers = NULL;
801 if (priv->account_mgr) {
802 g_object_unref (G_OBJECT(priv->account_mgr));
803 priv->account_mgr = NULL;
807 g_object_unref (G_OBJECT(priv->device));
811 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
812 if (priv->store_accounts) {
813 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
814 g_object_unref (priv->store_accounts);
815 priv->store_accounts = NULL;
818 if (priv->transport_accounts) {
819 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
820 g_object_unref (priv->transport_accounts);
821 priv->transport_accounts = NULL;
824 if (priv->store_accounts_outboxes) {
825 g_object_unref (priv->store_accounts_outboxes);
826 priv->store_accounts_outboxes = NULL;
830 camel_object_unref (CAMEL_OBJECT(priv->session));
831 priv->session = NULL;
834 G_OBJECT_CLASS(parent_class)->finalize (obj);
838 volume_path_is_mounted (const gchar* path)
840 g_return_val_if_fail (path, FALSE);
842 gboolean result = FALSE;
843 gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
844 g_return_val_if_fail (path_as_uri, FALSE);
846 /* Get the monitor singleton: */
847 GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
849 /* This seems like a simpler way to do this, but it returns a
850 * GnomeVFSVolume even if the drive is not mounted: */
852 GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor,
853 MODEST_MCC1_VOLUMEPATH);
855 gnome_vfs_volume_unref(volume);
859 /* Get the mounted volumes from the monitor: */
860 GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
862 for (iter = list; iter; iter = g_list_next (iter)) {
863 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
867 gnome_vfs_volume_get_display_name (volume);
868 printf ("volume display name=%s\n", display_name);
869 g_free (display_name);
873 gnome_vfs_volume_get_activation_uri (volume);
874 /* printf (" uri=%s\n", uri); */
875 if (uri && (strcmp (uri, path_as_uri) == 0))
880 gnome_vfs_volume_unref (volume);
886 g_free (path_as_uri);
891 ModestTnyAccountStore*
892 modest_tny_account_store_new (ModestAccountMgr *account_mgr,
896 ModestTnyAccountStorePrivate *priv;
897 TnyAccount *local_account = NULL;
898 TnyLockable *lockable;
900 g_return_val_if_fail (account_mgr, NULL);
901 g_return_val_if_fail (device, NULL);
903 obj = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
904 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
906 priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
907 priv->device = g_object_ref (device);
909 priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
910 if (!priv->session) {
911 g_warning ("failed to get TnySessionCamel");
915 /* Set the ui locker */
916 lockable = tny_gtk_lockable_new ();
917 tny_session_camel_set_ui_locker (priv->session, lockable);
918 g_object_unref (lockable);
920 /* Connect signals */
921 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
922 G_OBJECT(account_mgr), "account_inserted",
923 G_CALLBACK (on_account_inserted), obj);
924 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
925 G_OBJECT(account_mgr), "account_changed",
926 G_CALLBACK (on_account_changed), obj);
927 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
928 G_OBJECT(account_mgr), "account_removed",
929 G_CALLBACK (on_account_removed), obj);
931 /* Create the lists of accounts */
932 priv->store_accounts = tny_simple_list_new ();
933 priv->transport_accounts = tny_simple_list_new ();
934 priv->store_accounts_outboxes = tny_simple_list_new ();
936 /* Create the local folders account */
938 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
939 tny_list_append (priv->store_accounts, G_OBJECT(local_account));
940 g_object_unref (local_account);
942 /* Add the other remote accounts. Do this after adding the
943 local account, because we need to add our outboxes to the
944 global OUTBOX hosted in the local account */
945 add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
947 /* Add connection-specific transport accounts if there are any
948 accounts available */
949 if (!only_local_accounts (MODEST_TNY_ACCOUNT_STORE(obj)))
950 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
952 /* This is a singleton, so it does not need to be unrefed. */
953 if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
955 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */);
958 return MODEST_TNY_ACCOUNT_STORE(obj);
962 modest_tny_account_store_get_accounts (TnyAccountStore *self,
964 TnyGetAccountsRequestType request_type)
966 ModestTnyAccountStorePrivate *priv;
968 g_return_if_fail (self);
969 g_return_if_fail (TNY_IS_LIST(list));
971 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
973 switch (request_type) {
974 case TNY_ACCOUNT_STORE_BOTH:
975 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
976 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
978 case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
979 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
981 case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
982 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
985 g_return_if_reached ();
988 /* Initialize session. Why do we need this ??? */
989 tny_session_camel_set_initialized (priv->session);
994 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
996 ModestTnyAccountStorePrivate *priv;
997 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
999 if (!priv->cache_dir)
1000 priv->cache_dir = g_build_filename (g_get_home_dir(),
1001 MODEST_DIR, MODEST_CACHE_DIR, NULL);
1002 return priv->cache_dir;
1007 * callers need to unref
1010 modest_tny_account_store_get_device (TnyAccountStore *self)
1012 ModestTnyAccountStorePrivate *priv;
1014 g_return_val_if_fail (self, NULL);
1016 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1019 return g_object_ref (G_OBJECT(priv->device));
1026 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1028 return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self),
1029 MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1036 modest_tny_account_store_alert (TnyAccountStore *self,
1037 TnyAccount *account,
1042 ModestProtocolType protocol_type = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID;
1043 ModestProtocol *protocol = NULL;
1044 const gchar* server_name = "";
1045 gchar *prompt = NULL;
1046 gboolean retval = TRUE;
1048 /* NOTE: account may be NULL in some cases */
1049 g_return_val_if_fail (error, FALSE);
1051 /* Get the server name: */
1053 server_name = tny_account_get_hostname (account);
1054 protocol_type = modest_tny_account_get_protocol_type (account);
1055 if (protocol_type == MODEST_PROTOCOL_REGISTRY_TYPE_INVALID){
1056 g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__,
1057 tny_account_get_id (account));
1060 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
1064 switch (error->code) {
1065 case TNY_SYSTEM_ERROR_CANCEL:
1066 /* Don't show waste the user's time by showing him a dialog telling
1067 * him that he has just cancelled something: */
1070 case TNY_SERVICE_ERROR_PROTOCOL:
1071 /* Like a BAD from IMAP (protocol error) */
1072 case TNY_SERVICE_ERROR_LOST_CONNECTION:
1073 /* Lost the connection with the service */
1074 case TNY_SERVICE_ERROR_UNAVAILABLE:
1075 /* You must be working online for this operation */
1076 case TNY_SERVICE_ERROR_CONNECT:
1078 prompt = modest_protocol_get_translation (protocol, MODEST_PROTOCOL_TRANSLATION_CONNECT_ERROR, server_name);
1081 g_return_val_if_reached (FALSE);
1085 case TNY_SERVICE_ERROR_AUTHENTICATE:
1086 /* It seems that there's no better error to show with
1087 * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1088 * may appear if there's a timeout during auth */
1090 prompt = modest_protocol_get_translation (protocol, MODEST_PROTOCOL_TRANSLATION_AUTH_ERROR, server_name);
1093 g_return_val_if_reached (FALSE);
1097 case TNY_SERVICE_ERROR_CERTIFICATE:
1098 /* We'll show the proper dialog later */
1101 case TNY_SYSTEM_ERROR_MEMORY:
1102 /* Can't allocate memory for this operation */
1104 case TNY_SERVICE_ERROR_UNKNOWN:
1107 g_debug ("Unexpected error %d", error->code);
1108 g_return_val_if_reached (FALSE);
1112 if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1113 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1115 else if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE) {
1116 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1118 /* Show the account dialog if it was wrong */
1119 if (error->code == TNY_SERVICE_ERROR_CONNECT ||
1120 error->code == TNY_SERVICE_ERROR_AUTHENTICATE)
1121 show_wrong_password_dialog (account);
1126 g_debug ("%s: error code %d (%s", __FUNCTION__, error->code, error->message);
1136 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1138 TnyAccountStoreIface *klass;
1140 g_return_if_fail (g);
1142 klass = (TnyAccountStoreIface *)g;
1144 klass->get_accounts =
1145 modest_tny_account_store_get_accounts;
1146 klass->get_cache_dir =
1147 modest_tny_account_store_get_cache_dir;
1149 modest_tny_account_store_get_device;
1151 modest_tny_account_store_alert;
1152 klass->find_account =
1153 modest_tny_account_store_find_account_by_url;
1157 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1158 ModestTnyGetPassFunc func)
1160 /* not implemented, we use signals */
1161 g_printerr ("modest: set_get_pass_func not implemented\n");
1165 modest_tny_account_store_get_session (TnyAccountStore *self)
1167 g_return_val_if_fail (self, NULL);
1168 return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1172 get_tny_account_by (TnyList *accounts,
1173 ModestTnyAccountStoreQueryType type,
1176 TnyIterator *iter = NULL;
1177 gboolean found = FALSE;
1178 TnyAccount *retval = NULL;
1180 g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1182 if (tny_list_get_length(accounts) == 0) {
1183 g_warning ("%s: account list is empty", __FUNCTION__);
1187 iter = tny_list_create_iterator (accounts);
1188 while (!tny_iterator_is_done (iter) && !found) {
1189 TnyAccount *tmp_account = NULL;
1190 const gchar *val = NULL;
1192 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1193 if (!TNY_IS_ACCOUNT(tmp_account)) {
1194 g_warning ("%s: not a valid account", __FUNCTION__);
1200 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1201 val = tny_account_get_id (tmp_account);
1203 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1204 val = tny_account_get_url_string (tmp_account);
1208 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL &&
1209 tny_account_matches_url_string (tmp_account, str)) {
1210 retval = g_object_ref (tmp_account);
1213 if (val && str && strcmp (val, str) == 0) {
1214 retval = g_object_ref (tmp_account);
1218 g_object_unref (tmp_account);
1219 tny_iterator_next (iter);
1221 g_object_unref (iter);
1227 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self,
1228 ModestTnyAccountStoreQueryType type,
1231 TnyAccount *account = NULL;
1232 ModestTnyAccountStorePrivate *priv;
1234 g_return_val_if_fail (self, NULL);
1235 g_return_val_if_fail (str, NULL);
1237 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1239 /* Search in store accounts */
1240 account = get_tny_account_by (priv->store_accounts, type, str);
1242 /* If we already found something, no need to search the transport accounts */
1244 account = get_tny_account_by (priv->transport_accounts, type, str);
1246 /* If we already found something, no need to search the
1247 per-account outbox accounts */
1249 account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1252 /* Warn if nothing was found. This is generally unusual. */
1254 g_warning("%s: Failed to find account with %s=%s\n",
1256 (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",
1260 /* Returns a new reference to the account if found */
1266 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1267 const gchar *account_name,
1268 TnyAccountType type)
1270 ModestTnyAccountStorePrivate *priv = NULL;
1271 TnyAccount *retval = NULL;
1272 TnyList *account_list = NULL;
1273 TnyIterator *iter = NULL;
1276 g_return_val_if_fail (self, NULL);
1277 g_return_val_if_fail (account_name, NULL);
1278 g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE ||
1279 type == TNY_ACCOUNT_TYPE_TRANSPORT,
1282 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1284 account_list = (type == TNY_ACCOUNT_TYPE_STORE) ?
1285 priv->store_accounts :
1286 priv->transport_accounts;
1288 if (!account_list) {
1289 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__,
1290 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1294 /* Look for the server account */
1296 iter = tny_list_create_iterator (account_list);
1297 while (!tny_iterator_is_done (iter) && !found) {
1298 const gchar *modest_acc_name;
1299 TnyAccount *tmp_account;
1301 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1303 modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1305 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1307 retval = g_object_ref (tmp_account);
1309 /* Free and continue */
1310 g_object_unref (tmp_account);
1311 tny_iterator_next (iter);
1313 g_object_unref (iter);
1316 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1317 "Number of server accounts of this type=%d\n", __FUNCTION__,
1318 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1319 account_name, tny_list_get_length (account_list));
1322 /* Returns a new reference */
1327 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1328 ModestTnyAccountStore *self, const gchar *account_name)
1332 g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1333 g_return_val_if_fail (account_name, NULL);
1335 /* Get the current connection: */
1336 device = modest_runtime_get_device ();
1339 g_warning ("%s: could not get device", __FUNCTION__);
1343 if (!tny_device_is_online (device))
1346 #ifdef MODEST_HAVE_CONIC
1347 g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1349 TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);
1350 const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1351 /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1355 ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1359 const gchar *connection_id = con_ic_iap_get_id (connection);
1360 /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1364 /* Get the connection-specific transport acccount, if any: */
1365 ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1367 /* Check if this account has connection-specific SMTP enabled */
1368 if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1372 gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager,
1375 /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1376 if (!server_account_name) {
1377 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1380 TnyAccount* account = modest_tny_account_store_get_tny_account_by (self,
1381 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1382 server_account_name);
1384 /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1385 g_free (server_account_name);
1387 /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1388 g_object_unref (connection);
1392 return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1393 #endif /* MODEST_HAVE_CONIC */
1398 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1399 const gchar *account_name)
1401 g_return_val_if_fail (self, NULL);
1402 g_return_val_if_fail (account_name, NULL);
1404 if (!account_name || !self)
1407 /* Get the connection-specific transport acccount, if any: */
1408 /* Note: This gives us a reference: */
1409 TnyAccount *account =
1410 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1412 /* If there is no connection-specific transport account (the common case),
1413 * just get the regular transport account: */
1415 /* The special local folders don't have transport accounts. */
1416 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1419 /* Note: This gives us a reference: */
1420 account = modest_tny_account_store_get_server_account (self, account_name,
1421 TNY_ACCOUNT_TYPE_TRANSPORT);
1425 /* returns a reference. */
1430 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1432 TnyAccount *account = NULL;
1433 ModestTnyAccountStorePrivate *priv;
1437 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1439 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1442 iter = tny_list_create_iterator (priv->store_accounts);
1443 while (!tny_iterator_is_done (iter) && !found) {
1444 TnyAccount *tmp_account;
1446 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1447 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1448 account = g_object_ref (tmp_account);
1451 g_object_unref (tmp_account);
1452 tny_iterator_next (iter);
1454 g_object_unref (iter);
1456 /* Returns a new reference to the account */
1461 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1463 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1466 return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1467 MODEST_MMC_ACCOUNT_ID);
1471 /*********************************************************************************/
1473 add_existing_accounts (ModestTnyAccountStore *self)
1475 GSList *account_names = NULL, *iter = NULL;
1476 ModestTnyAccountStorePrivate *priv = NULL;
1478 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1480 /* These are account names, not server_account names */
1481 account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1483 for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1484 const gchar *account_name = (const gchar*) iter->data;
1486 /* Insert all enabled accounts without notifying */
1487 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1488 insert_account (self, account_name, FALSE);
1490 modest_account_mgr_free_account_names (account_names);
1494 connection_status_changed (TnyAccount *account,
1495 TnyConnectionStatus status,
1498 /* We do this here and not in the connection policy because we
1499 don't want to do it for every account, just for the
1500 accounts that are interactively added when modest is
1502 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1503 const gchar *account_name;
1504 ModestWindow *main_window;
1505 ModestTnyAccountStorePrivate *priv = NULL;
1507 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1509 /* Remove this handler */
1510 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1512 "connection_status_changed");
1514 /* Perform a send receive */
1515 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1516 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1517 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, TRUE, main_window);
1522 create_tny_account (ModestTnyAccountStore *self,
1524 TnyAccountType type,
1527 TnyAccount *account = NULL;
1528 ModestTnyAccountStorePrivate *priv = NULL;
1530 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1532 account = modest_tny_account_new_from_account (priv->account_mgr,
1539 /* Forget any cached password for the account, so that
1540 we use a new account if any */
1541 forget_password_in_memory (self, tny_account_get_id (account));
1543 /* Install a signal handler that will refresh the
1544 account the first time it becomes online. Do this
1545 only if we're adding a new account while the
1546 program is running (we do not want to do this
1548 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1549 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1551 "connection_status_changed",
1552 G_CALLBACK (connection_status_changed),
1555 /* Set the account store */
1556 g_object_set_data (G_OBJECT(account), "account_store", self);
1558 g_printerr ("modest: failed to create account for %s\n", name);
1564 typedef struct _AddOutboxInfo {
1565 ModestTnyAccountStore *account_store;
1566 TnyAccount *transport_account;
1570 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1576 TnyIterator *iter_folders;
1577 TnyFolder *per_account_outbox;
1578 TnyAccount *local_account = NULL;
1579 AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1580 ModestTnyAccountStorePrivate *priv = NULL;
1581 ModestTnyAccountStore *self;
1583 self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1584 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1586 /* Note that this could happen if there is not enough space
1587 available on disk, then the outbox folder could not be
1589 if (tny_list_get_length (list) != 1) {
1590 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1591 tny_list_get_length (list));
1595 iter_folders = tny_list_create_iterator (list);
1596 per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1597 g_object_unref (iter_folders);
1598 g_object_unref (list);
1600 /* Add the outbox of the new per-account-local-outbox account
1601 to the global local merged OUTBOX of the local folders
1603 local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1604 modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1605 per_account_outbox);
1606 /* Add the pair to the hash table */
1607 g_hash_table_insert (priv->outbox_of_transport,
1608 info->transport_account,
1609 per_account_outbox);
1611 /* Notify that the local account changed */
1612 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1613 g_object_unref (local_account);
1614 g_object_unref (per_account_outbox);
1617 g_object_unref (info->transport_account);
1618 g_slice_free (AddOutboxInfo, info);
1623 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1624 const gchar *account_name,
1625 TnyAccount *transport_account)
1627 TnyList *folders = NULL;
1628 TnyAccount *account_outbox = NULL;
1629 ModestTnyAccountStorePrivate *priv = NULL;
1630 AddOutboxInfo *info;
1632 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1634 /* Create per account local outbox */
1636 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr,
1640 if (!G_IS_OBJECT (account_outbox)) {
1641 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1645 tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1647 /* Get the outbox folder */
1648 folders = tny_simple_list_new ();
1649 info = g_slice_new0 (AddOutboxInfo);
1650 info->account_store = self;
1651 info->transport_account = g_object_ref (transport_account);
1652 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL,
1653 add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1654 g_object_unref (account_outbox);
1658 * This function will be used for both adding new accounts and for the
1659 * initialization. In the initialization we do not want to emit
1660 * signals so notify will be FALSE, in the case of account additions
1661 * we do want to notify the observers
1664 insert_account (ModestTnyAccountStore *self,
1665 const gchar *account,
1668 ModestTnyAccountStorePrivate *priv = NULL;
1669 TnyAccount *store_account = NULL, *transport_account = NULL;
1671 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1673 /* Get the server and the transport account */
1674 store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1675 if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1676 g_warning ("%s: failed to create store account", __FUNCTION__);
1680 transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1681 if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1682 g_warning ("%s: failed to create transport account", __FUNCTION__);
1683 g_object_unref (store_account);
1687 /* Add accounts to the lists */
1688 tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1689 tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1691 /* Create a new pseudo-account with an outbox for this
1692 transport account and add it to the global outbox
1693 in the local account */
1694 add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1696 /* Notify the observers. We do it after everything is
1699 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1700 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1704 g_object_unref (store_account);
1705 g_object_unref (transport_account);
1709 only_local_accounts (ModestTnyAccountStore *self)
1711 ModestTnyAccountStorePrivate *priv = NULL;
1712 gboolean only_local = TRUE;
1715 /* Check if this is the first remote account we add */
1716 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1717 iter = tny_list_create_iterator (priv->store_accounts);
1719 while (!tny_iterator_is_done (iter) && only_local) {
1720 TnyAccount *account = (TnyAccount *) tny_iterator_get_current (iter);
1721 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1723 g_object_unref (account);
1724 tny_iterator_next (iter);
1726 g_object_unref (iter);
1732 on_account_inserted (ModestAccountMgr *acc_mgr,
1733 const gchar *account,
1736 gboolean add_specific;
1738 add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1740 /* Insert the account and notify the observers */
1741 insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1743 /* If it's the first remote account then add the connection
1744 specific SMTP servers as well */
1746 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1750 /* This is the callback of the tny_camel_account_set_online called in
1751 on_account_removed to disconnect the account */
1753 on_account_disconnect_when_removing (TnyCamelAccount *account,
1758 ModestTnyAccountStore *self;
1759 ModestTnyAccountStorePrivate *priv;
1761 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1762 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1764 /* Remove the connection-status-changed handler if it's still there */
1765 if (modest_signal_mgr_is_connected (priv->sighandlers,
1767 "connection_status_changed")) {
1768 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1770 "connection_status_changed");
1773 /* Cancel all pending operations */
1774 tny_account_cancel (TNY_ACCOUNT (account));
1776 /* Unref the extra reference added by get_server_account */
1777 g_object_unref (account);
1779 /* Clear the cache if it's an store account */
1780 if (TNY_IS_STORE_ACCOUNT (account)) {
1781 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1782 } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1783 ModestTnySendQueue* send_queue;
1784 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1785 if (TNY_IS_SEND_QUEUE (send_queue)) {
1786 if (modest_tny_send_queue_sending_in_progress (send_queue))
1787 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1788 TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE,
1790 modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1796 * We use this one for both removing "normal" and "connection
1797 * specific" transport accounts
1800 remove_transport_account (ModestTnyAccountStore *self,
1801 TnyTransportAccount *transport_account)
1803 ModestTnyAccountStorePrivate *priv;
1804 TnyFolder *outbox = NULL;
1806 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1808 /* Remove it from the list of accounts and notify the
1809 observers. Do not need to wait for account
1811 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1812 tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1814 /* Remove the OUTBOX of the account from the global outbox */
1815 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1817 if (TNY_IS_FOLDER (outbox)) {
1818 TnyAccount *local_account = NULL;
1819 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1821 if (outbox_account) {
1822 tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1823 /* Remove existing emails to send */
1824 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1825 g_object_unref (outbox_account);
1828 local_account = modest_tny_account_store_get_local_folders_account (self);
1829 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1832 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1834 /* Notify the change in the local account */
1835 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1836 g_object_unref (local_account);
1838 g_warning ("Removing a transport account that has no outbox");
1841 /* Cancel all pending operations */
1842 tny_account_cancel (TNY_ACCOUNT (transport_account));
1844 /* Disconnect and notify the observers. The callback will free the reference */
1845 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1846 on_account_disconnect_when_removing, self);
1850 images_cache_remove_filter (TnyStreamCache *self, const gchar *id, const gchar *account_name)
1852 gchar *account_name_with_separator;
1854 if (account_name == NULL || account_name[0] == '\0')
1857 if (id == NULL || id[0] == '\0')
1860 account_name_with_separator = g_strconcat (account_name, "__", NULL);
1862 result = (g_str_has_prefix (id, account_name));
1863 g_free (account_name_with_separator);
1869 on_account_removed (ModestAccountMgr *acc_mgr,
1870 const gchar *account,
1873 TnyAccount *store_account = NULL, *transport_account = NULL;
1874 ModestTnyAccountStore *self;
1875 ModestTnyAccountStorePrivate *priv;
1876 TnyStreamCache *stream_cache;
1878 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1879 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1881 /* Get the server and the transport account */
1883 modest_tny_account_store_get_server_account (self, account,
1884 TNY_ACCOUNT_TYPE_STORE);
1886 modest_tny_account_store_get_server_account (self, account,
1887 TNY_ACCOUNT_TYPE_TRANSPORT);
1889 /* If there was any problem creating the account, for example,
1890 with the configuration system this could not exist */
1891 if (TNY_IS_STORE_ACCOUNT(store_account)) {
1892 /* Forget any cached password for the account */
1893 forget_password_in_memory (self, tny_account_get_id (store_account));
1895 /* Remove it from the list of accounts and notify the
1896 observers. Do not need to wait for account
1898 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1899 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1901 /* Cancel all pending operations */
1902 tny_account_cancel (TNY_ACCOUNT (store_account));
1904 /* Disconnect before deleting the cache, because the
1905 disconnection will rewrite the cache to the
1907 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1908 on_account_disconnect_when_removing, self);
1910 g_warning ("%s: no store account for account %s\n",
1911 __FUNCTION__, account);
1914 /* If there was any problem creating the account, for example,
1915 with the configuration system this could not exist */
1916 if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1918 /* Forget any cached password for the account */
1919 forget_password_in_memory (self, tny_account_get_id (transport_account));
1921 /* Remove transport account. It'll free the reference
1922 added by get_server_account */
1923 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1925 g_warning ("%s: no transport account for account %s\n",
1926 __FUNCTION__, account);
1929 /* Remove cached images */
1930 stream_cache = modest_runtime_get_images_cache ();
1931 tny_stream_cache_remove (stream_cache, (TnyStreamCacheRemoveFilter) images_cache_remove_filter, (gpointer) account);
1933 /* If there are no more user accounts then delete the
1934 transport specific SMTP servers */
1935 if (only_local_accounts (self))
1936 remove_connection_specific_transport_accounts (self);
1939 TnyTransportAccount *
1940 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1943 ModestTnyAccountStorePrivate *priv = NULL;
1944 TnyAccount * tny_account = NULL;
1946 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1948 /* Add the account: */
1950 modest_tny_account_new_from_server_account_name (priv->account_mgr,
1956 g_object_set_data (G_OBJECT(tny_account),
1959 g_object_set_data (G_OBJECT(tny_account),
1960 "connection_specific",
1961 GINT_TO_POINTER (TRUE));
1963 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1964 add_outbox_from_transport_account_to_global_outbox (self,
1969 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1972 return TNY_TRANSPORT_ACCOUNT (tny_account);
1977 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1979 ModestTnyAccountStorePrivate *priv = NULL;
1980 GSList *list_specifics = NULL, *iter = NULL;
1983 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1985 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
1986 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1987 MODEST_CONF_VALUE_STRING, &err);
1990 g_return_if_reached ();
1993 /* Look at each connection-specific transport account for the
1994 * modest account: */
1995 iter = list_specifics;
1997 /* The list alternates between the connection name and the transport name: */
1998 iter = g_slist_next (iter);
2000 const gchar* transport_account_name = (const gchar*) (iter->data);
2001 TnyTransportAccount * account = NULL;
2002 account = modest_tny_account_store_new_connection_specific_transport_account (
2003 self, transport_account_name);
2005 g_object_unref (account);
2007 iter = g_slist_next (iter);
2012 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2014 ModestTnyAccountStorePrivate *priv = NULL;
2015 GSList *list_specifics = NULL, *iter = NULL;
2018 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2021 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2022 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2023 MODEST_CONF_VALUE_STRING, &err);
2026 g_return_if_reached ();
2029 /* Look at each connection-specific transport account for the
2030 * modest account: */
2031 iter = list_specifics;
2033 /* The list alternates between the connection name and the transport name: */
2034 iter = g_slist_next (iter);
2036 const gchar* transport_account_name = (const gchar*) (iter->data);
2037 TnyAccount * account;
2038 account = modest_tny_account_store_get_server_account (self,
2039 transport_account_name,
2040 TNY_ACCOUNT_TYPE_TRANSPORT);
2042 /* the call will free the reference */
2044 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2046 iter = g_slist_next (iter);
2052 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self,
2054 TnyAccount **ac_out)
2056 TnyIterator *acc_iter;
2057 ModestTnyAccountStorePrivate *priv;
2059 TnyAccount *msg_account = NULL;
2061 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2062 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2064 acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2065 while (!msg && !tny_iterator_is_done (acc_iter)) {
2066 TnyList *folders = tny_simple_list_new ();
2067 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2068 TnyIterator *folders_iter = NULL;
2070 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
2071 folders_iter = tny_list_create_iterator (folders);
2073 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2074 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2075 msg = tny_folder_find_msg (folder, uri, NULL);
2078 msg_account = g_object_ref (account);
2080 g_object_unref (folder);
2081 tny_iterator_next (folders_iter);
2083 g_object_unref (folders_iter);
2085 g_object_unref (folders);
2086 g_object_unref (account);
2087 tny_iterator_next (acc_iter);
2090 g_object_unref (acc_iter);
2093 *ac_out = msg_account;
2098 TnyTransportAccount *
2099 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2101 TnyIterator *acc_iter;
2102 ModestTnyAccountStorePrivate *priv;
2103 TnyTransportAccount *header_acc = NULL;
2106 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2107 g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2108 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2109 msg_id = modest_tny_send_queue_get_msg_id (header);
2110 acc_iter = tny_list_create_iterator (priv->transport_accounts);
2111 while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2112 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2113 ModestTnySendQueue *send_queue;
2114 ModestTnySendQueueStatus status;
2115 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2116 if (TNY_IS_SEND_QUEUE (send_queue)) {
2117 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2118 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2119 header_acc = g_object_ref(account);
2121 g_object_unref (account);
2122 tny_iterator_next (acc_iter);
2124 g_object_unref(acc_iter);
2132 ModestTnyAccountStore *account_store;
2133 ModestTnyAccountStoreShutdownCallback callback;
2139 account_shutdown_callback (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer userdata)
2141 ShutdownOpData *op_data = (ShutdownOpData *) userdata;
2143 if (op_data->pending == 0) {
2144 if (op_data->callback)
2145 op_data->callback (op_data->account_store, op_data->userdata);
2146 g_object_unref (op_data->account_store);
2149 g_object_unref (op_data->account_store);
2154 account_shutdown (TnyAccount *account, ShutdownOpData *op_data)
2156 g_return_if_fail (account && TNY_IS_ACCOUNT(account));
2158 if (TNY_IS_STORE_ACCOUNT (account) &&
2159 !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
2162 /* Disconnect account */
2163 if (tny_account_get_connection_status (account) == TNY_CONNECTION_STATUS_CONNECTED) {
2164 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE,
2165 account_shutdown_callback, op_data);
2171 g_object_unref (op_data->account_store);
2176 modest_tny_account_store_shutdown (ModestTnyAccountStore *self,
2177 ModestTnyAccountStoreShutdownCallback callback,
2180 gint i, num_accounts;
2181 ShutdownOpData *op_data;
2182 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2184 /* Get references */
2185 num_accounts = tny_list_get_length (priv->store_accounts) +
2186 tny_list_get_length (priv->transport_accounts);
2187 for (i = 0 ; i < num_accounts ; i++)
2188 g_object_ref (self);
2190 /* Create the helper object */
2191 op_data = g_new0 (ShutdownOpData, 1);
2192 op_data->callback = callback;
2193 op_data->userdata = userdata;
2194 op_data->pending = num_accounts;
2195 op_data->account_store = self;
2197 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
2198 if (priv->store_accounts) {
2199 tny_list_foreach (priv->store_accounts, (GFunc)account_shutdown, op_data);
2202 if (priv->transport_accounts) {
2203 tny_list_foreach (priv->transport_accounts, (GFunc)account_shutdown, op_data);
2206 if (op_data->pending == 0) {
2207 if (op_data->callback)
2208 op_data->callback (op_data->account_store, op_data->userdata);
2214 modest_tny_account_store_is_send_mail_blocked (ModestTnyAccountStore *self)
2216 ModestTnyAccountStorePrivate *priv;
2218 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2220 return priv->send_mail_blocked;
2224 modest_tny_account_store_set_send_mail_blocked (ModestTnyAccountStore *self,
2227 ModestTnyAccountStorePrivate *priv;
2229 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2231 priv->send_mail_blocked = blocked;