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;
901 g_return_val_if_fail (account_mgr, NULL);
902 g_return_val_if_fail (device, NULL);
904 obj = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
905 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
907 priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
908 priv->device = g_object_ref (device);
910 priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
911 if (!priv->session) {
912 g_warning ("failed to get TnySessionCamel");
916 /* Set the ui locker */
917 tny_session_camel_set_ui_locker (priv->session, tny_gtk_lockable_new ());
919 /* Connect signals */
920 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
921 G_OBJECT(account_mgr), "account_inserted",
922 G_CALLBACK (on_account_inserted), obj);
923 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
924 G_OBJECT(account_mgr), "account_changed",
925 G_CALLBACK (on_account_changed), obj);
926 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
927 G_OBJECT(account_mgr), "account_removed",
928 G_CALLBACK (on_account_removed), obj);
930 /* Create the lists of accounts */
931 priv->store_accounts = tny_simple_list_new ();
932 priv->transport_accounts = tny_simple_list_new ();
933 priv->store_accounts_outboxes = tny_simple_list_new ();
935 /* Create the local folders account */
937 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
938 tny_list_append (priv->store_accounts, G_OBJECT(local_account));
939 g_object_unref (local_account);
941 /* Add the other remote accounts. Do this after adding the
942 local account, because we need to add our outboxes to the
943 global OUTBOX hosted in the local account */
944 add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
946 /* Add connection-specific transport accounts if there are any
947 accounts available */
948 if (!only_local_accounts (MODEST_TNY_ACCOUNT_STORE(obj)))
949 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
951 /* This is a singleton, so it does not need to be unrefed. */
952 if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
954 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */);
957 return MODEST_TNY_ACCOUNT_STORE(obj);
961 modest_tny_account_store_get_accounts (TnyAccountStore *self,
963 TnyGetAccountsRequestType request_type)
965 ModestTnyAccountStorePrivate *priv;
967 g_return_if_fail (self);
968 g_return_if_fail (TNY_IS_LIST(list));
970 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
972 switch (request_type) {
973 case TNY_ACCOUNT_STORE_BOTH:
974 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
975 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
977 case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
978 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
980 case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
981 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
984 g_return_if_reached ();
987 /* Initialize session. Why do we need this ??? */
988 tny_session_camel_set_initialized (priv->session);
993 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
995 ModestTnyAccountStorePrivate *priv;
996 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
998 if (!priv->cache_dir)
999 priv->cache_dir = g_build_filename (g_get_home_dir(),
1000 MODEST_DIR, MODEST_CACHE_DIR, NULL);
1001 return priv->cache_dir;
1006 * callers need to unref
1009 modest_tny_account_store_get_device (TnyAccountStore *self)
1011 ModestTnyAccountStorePrivate *priv;
1013 g_return_val_if_fail (self, NULL);
1015 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1018 return g_object_ref (G_OBJECT(priv->device));
1025 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1027 return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self),
1028 MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1035 modest_tny_account_store_alert (TnyAccountStore *self,
1036 TnyAccount *account,
1041 ModestTransportStoreProtocol proto =
1042 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
1043 const gchar* server_name = "";
1044 gchar *prompt = NULL;
1047 /* NOTE: account may be NULL in some cases */
1048 g_return_val_if_fail (error, FALSE);
1050 /* Get the server name: */
1052 server_name = tny_account_get_hostname (account);
1053 const gchar *proto_name = tny_account_get_proto (account);
1055 proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1057 g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__,
1058 tny_account_get_id (account));
1063 switch (error->code) {
1064 case TNY_SYSTEM_ERROR_CANCEL:
1065 /* Don't show waste the user's time by showing him a dialog telling
1066 * him that he has just cancelled something: */
1069 case TNY_SERVICE_ERROR_PROTOCOL:
1070 /* Like a BAD from IMAP (protocol error) */
1071 case TNY_SERVICE_ERROR_LOST_CONNECTION:
1072 /* Lost the connection with the service */
1073 case TNY_SERVICE_ERROR_UNAVAILABLE:
1074 /* You must be working online for this operation */
1075 case TNY_SERVICE_ERROR_CONNECT:
1077 case MODEST_PROTOCOL_STORE_POP:
1078 prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1081 case MODEST_PROTOCOL_STORE_IMAP:
1082 prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1085 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1086 prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1090 g_return_val_if_reached (FALSE);
1094 case TNY_SERVICE_ERROR_AUTHENTICATE:
1095 /* It seems that there's no better error to show with
1096 * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1097 * may appear if there's a timeout during auth */
1099 case MODEST_PROTOCOL_STORE_POP:
1100 prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1103 case MODEST_PROTOCOL_STORE_IMAP:
1104 prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1107 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1108 prompt = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
1112 g_return_val_if_reached (FALSE);
1116 case TNY_SERVICE_ERROR_CERTIFICATE:
1117 /* We'll show the proper dialog later */
1120 case TNY_SYSTEM_ERROR_MEMORY:
1121 /* Can't allocate memory for this operation */
1123 case TNY_SERVICE_ERROR_UNKNOWN:
1126 g_debug ("Unexpected error %d", error->code);
1127 g_return_val_if_reached (FALSE);
1131 if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1132 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1135 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1137 /* Show the account dialog if it was wrong */
1138 if (error->code == TNY_SERVICE_ERROR_CONNECT ||
1139 error->code == TNY_SERVICE_ERROR_AUTHENTICATE)
1140 show_wrong_password_dialog (account);
1154 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1156 TnyAccountStoreIface *klass;
1158 g_return_if_fail (g);
1160 klass = (TnyAccountStoreIface *)g;
1162 klass->get_accounts =
1163 modest_tny_account_store_get_accounts;
1164 klass->get_cache_dir =
1165 modest_tny_account_store_get_cache_dir;
1167 modest_tny_account_store_get_device;
1169 modest_tny_account_store_alert;
1170 klass->find_account =
1171 modest_tny_account_store_find_account_by_url;
1175 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1176 ModestTnyGetPassFunc func)
1178 /* not implemented, we use signals */
1179 g_printerr ("modest: set_get_pass_func not implemented\n");
1183 modest_tny_account_store_get_session (TnyAccountStore *self)
1185 g_return_val_if_fail (self, NULL);
1186 return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1190 get_tny_account_by (TnyList *accounts,
1191 ModestTnyAccountStoreQueryType type,
1194 TnyIterator *iter = NULL;
1195 gboolean found = FALSE;
1196 TnyAccount *retval = NULL;
1198 g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1200 if (tny_list_get_length(accounts) == 0) {
1201 g_warning ("%s: account list is empty", __FUNCTION__);
1205 iter = tny_list_create_iterator (accounts);
1206 while (!tny_iterator_is_done (iter) && !found) {
1207 TnyAccount *tmp_account = NULL;
1208 const gchar *val = NULL;
1210 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1211 if (!TNY_IS_ACCOUNT(tmp_account)) {
1212 g_warning ("%s: not a valid account", __FUNCTION__);
1218 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1219 val = tny_account_get_id (tmp_account);
1221 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1222 val = tny_account_get_url_string (tmp_account);
1226 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL &&
1227 tny_account_matches_url_string (tmp_account, str)) {
1228 retval = g_object_ref (tmp_account);
1231 if (val && str && strcmp (val, str) == 0) {
1232 retval = g_object_ref (tmp_account);
1236 g_object_unref (tmp_account);
1237 tny_iterator_next (iter);
1239 g_object_unref (iter);
1245 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self,
1246 ModestTnyAccountStoreQueryType type,
1249 TnyAccount *account = NULL;
1250 ModestTnyAccountStorePrivate *priv;
1252 g_return_val_if_fail (self, NULL);
1253 g_return_val_if_fail (str, NULL);
1255 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1257 /* Search in store accounts */
1258 account = get_tny_account_by (priv->store_accounts, type, str);
1260 /* If we already found something, no need to search the transport accounts */
1262 account = get_tny_account_by (priv->transport_accounts, type, str);
1264 /* If we already found something, no need to search the
1265 per-account outbox accounts */
1267 account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1270 /* Warn if nothing was found. This is generally unusual. */
1272 g_warning("%s: Failed to find account with %s=%s\n",
1274 (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",
1278 /* Returns a new reference to the account if found */
1284 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1285 const gchar *account_name,
1286 TnyAccountType type)
1288 ModestTnyAccountStorePrivate *priv = NULL;
1289 TnyAccount *retval = NULL;
1290 TnyList *account_list = NULL;
1291 TnyIterator *iter = NULL;
1294 g_return_val_if_fail (self, NULL);
1295 g_return_val_if_fail (account_name, NULL);
1296 g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE ||
1297 type == TNY_ACCOUNT_TYPE_TRANSPORT,
1300 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1302 account_list = (type == TNY_ACCOUNT_TYPE_STORE) ?
1303 priv->store_accounts :
1304 priv->transport_accounts;
1306 if (!account_list) {
1307 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__,
1308 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1312 /* Look for the server account */
1314 iter = tny_list_create_iterator (account_list);
1315 while (!tny_iterator_is_done (iter) && !found) {
1316 const gchar *modest_acc_name;
1317 TnyAccount *tmp_account;
1319 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1321 modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1323 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1325 retval = g_object_ref (tmp_account);
1327 /* Free and continue */
1328 g_object_unref (tmp_account);
1329 tny_iterator_next (iter);
1331 g_object_unref (iter);
1334 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1335 "Number of server accounts of this type=%d\n", __FUNCTION__,
1336 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1337 account_name, tny_list_get_length (account_list));
1340 /* Returns a new reference */
1345 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1346 ModestTnyAccountStore *self, const gchar *account_name)
1350 g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1351 g_return_val_if_fail (account_name, NULL);
1353 /* Get the current connection: */
1354 device = modest_runtime_get_device ();
1357 g_warning ("%s: could not get device", __FUNCTION__);
1361 if (!tny_device_is_online (device))
1364 #ifdef MODEST_HAVE_CONIC
1365 g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1367 TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);
1368 const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1369 /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1373 ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1377 const gchar *connection_id = con_ic_iap_get_id (connection);
1378 /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1382 /* Get the connection-specific transport acccount, if any: */
1383 ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1385 /* Check if this account has connection-specific SMTP enabled */
1386 if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1390 gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager,
1393 /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1394 if (!server_account_name) {
1395 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1398 TnyAccount* account = modest_tny_account_store_get_tny_account_by (self,
1399 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1400 server_account_name);
1402 /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1403 g_free (server_account_name);
1405 /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1406 g_object_unref (connection);
1410 return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1411 #endif /* MODEST_HAVE_CONIC */
1416 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1417 const gchar *account_name)
1419 g_return_val_if_fail (self, NULL);
1420 g_return_val_if_fail (account_name, NULL);
1422 if (!account_name || !self)
1425 /* Get the connection-specific transport acccount, if any: */
1426 /* Note: This gives us a reference: */
1427 TnyAccount *account =
1428 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1430 /* If there is no connection-specific transport account (the common case),
1431 * just get the regular transport account: */
1433 /* The special local folders don't have transport accounts. */
1434 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1437 /* Note: This gives us a reference: */
1438 account = modest_tny_account_store_get_server_account (self, account_name,
1439 TNY_ACCOUNT_TYPE_TRANSPORT);
1443 /* returns a reference. */
1448 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1450 TnyAccount *account = NULL;
1451 ModestTnyAccountStorePrivate *priv;
1455 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1457 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1460 iter = tny_list_create_iterator (priv->store_accounts);
1461 while (!tny_iterator_is_done (iter) && !found) {
1462 TnyAccount *tmp_account;
1464 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1465 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1466 account = g_object_ref (tmp_account);
1469 g_object_unref (tmp_account);
1470 tny_iterator_next (iter);
1472 g_object_unref (iter);
1474 /* Returns a new reference to the account */
1479 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1481 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1484 return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1485 MODEST_MMC_ACCOUNT_ID);
1489 /*********************************************************************************/
1491 add_existing_accounts (ModestTnyAccountStore *self)
1493 GSList *account_names = NULL, *iter = NULL;
1494 ModestTnyAccountStorePrivate *priv = NULL;
1496 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1498 /* These are account names, not server_account names */
1499 account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1501 for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1502 const gchar *account_name = (const gchar*) iter->data;
1504 /* Insert all enabled accounts without notifying */
1505 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1506 insert_account (self, account_name, FALSE);
1508 modest_account_mgr_free_account_names (account_names);
1512 connection_status_changed (TnyAccount *account,
1513 TnyConnectionStatus status,
1516 /* We do this here and not in the connection policy because we
1517 don't want to do it for every account, just for the
1518 accounts that are interactively added when modest is
1520 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1521 const gchar *account_name;
1522 ModestWindow *main_window;
1523 ModestTnyAccountStorePrivate *priv = NULL;
1525 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1527 /* Remove this handler */
1528 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1530 "connection_status_changed");
1532 /* Perform a send receive */
1533 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1534 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1535 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, TRUE, main_window);
1540 create_tny_account (ModestTnyAccountStore *self,
1542 TnyAccountType type,
1545 TnyAccount *account = NULL;
1546 ModestTnyAccountStorePrivate *priv = NULL;
1548 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1550 account = modest_tny_account_new_from_account (priv->account_mgr,
1557 /* Forget any cached password for the account, so that
1558 we use a new account if any */
1559 forget_password_in_memory (self, tny_account_get_id (account));
1561 /* Install a signal handler that will refresh the
1562 account the first time it becomes online. Do this
1563 only if we're adding a new account while the
1564 program is running (we do not want to do this
1566 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1567 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1569 "connection_status_changed",
1570 G_CALLBACK (connection_status_changed),
1573 /* Set the account store */
1574 g_object_set_data (G_OBJECT(account), "account_store", self);
1576 g_printerr ("modest: failed to create account for %s\n", name);
1582 typedef struct _AddOutboxInfo {
1583 ModestTnyAccountStore *account_store;
1584 TnyAccount *transport_account;
1588 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1594 TnyIterator *iter_folders;
1595 TnyFolder *per_account_outbox;
1596 TnyAccount *local_account = NULL;
1597 AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1598 ModestTnyAccountStorePrivate *priv = NULL;
1599 ModestTnyAccountStore *self;
1601 self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1602 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1604 /* Note that this could happen if there is not enough space
1605 available on disk, then the outbox folder could not be
1607 if (tny_list_get_length (list) != 1) {
1608 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1609 tny_list_get_length (list));
1613 iter_folders = tny_list_create_iterator (list);
1614 per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1615 g_object_unref (iter_folders);
1616 g_object_unref (list);
1618 /* Add the outbox of the new per-account-local-outbox account
1619 to the global local merged OUTBOX of the local folders
1621 local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1622 modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1623 per_account_outbox);
1624 /* Add the pair to the hash table */
1625 g_hash_table_insert (priv->outbox_of_transport,
1626 info->transport_account,
1627 per_account_outbox);
1629 /* Notify that the local account changed */
1630 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1631 g_object_unref (local_account);
1632 g_object_unref (per_account_outbox);
1635 g_object_unref (info->transport_account);
1636 g_slice_free (AddOutboxInfo, info);
1641 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1642 const gchar *account_name,
1643 TnyAccount *transport_account)
1645 TnyList *folders = NULL;
1646 TnyAccount *account_outbox = NULL;
1647 ModestTnyAccountStorePrivate *priv = NULL;
1648 AddOutboxInfo *info;
1650 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1652 /* Create per account local outbox */
1654 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr,
1658 if (!G_IS_OBJECT (account_outbox)) {
1659 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1663 tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1665 /* Get the outbox folder */
1666 folders = tny_simple_list_new ();
1667 info = g_slice_new0 (AddOutboxInfo);
1668 info->account_store = self;
1669 info->transport_account = g_object_ref (transport_account);
1670 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL,
1671 add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1672 g_object_unref (account_outbox);
1676 * This function will be used for both adding new accounts and for the
1677 * initialization. In the initialization we do not want to emit
1678 * signals so notify will be FALSE, in the case of account additions
1679 * we do want to notify the observers
1682 insert_account (ModestTnyAccountStore *self,
1683 const gchar *account,
1686 ModestTnyAccountStorePrivate *priv = NULL;
1687 TnyAccount *store_account = NULL, *transport_account = NULL;
1689 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1691 /* Get the server and the transport account */
1692 store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1693 if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1694 g_warning ("%s: failed to create store account", __FUNCTION__);
1698 transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1699 if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1700 g_warning ("%s: failed to create transport account", __FUNCTION__);
1701 g_object_unref (store_account);
1705 /* Add accounts to the lists */
1706 tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1707 tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1709 /* Create a new pseudo-account with an outbox for this
1710 transport account and add it to the global outbox
1711 in the local account */
1712 add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1714 /* Notify the observers. We do it after everything is
1717 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1718 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1722 g_object_unref (store_account);
1723 g_object_unref (transport_account);
1727 only_local_accounts (ModestTnyAccountStore *self)
1729 ModestTnyAccountStorePrivate *priv = NULL;
1730 gboolean only_local = TRUE;
1733 /* Check if this is the first remote account we add */
1734 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1735 iter = tny_list_create_iterator (priv->store_accounts);
1737 while (!tny_iterator_is_done (iter) && only_local) {
1738 TnyAccount *account = (TnyAccount *) tny_iterator_get_current (iter);
1739 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1741 g_object_unref (account);
1742 tny_iterator_next (iter);
1744 g_object_unref (iter);
1750 on_account_inserted (ModestAccountMgr *acc_mgr,
1751 const gchar *account,
1754 gboolean add_specific;
1756 add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1758 /* Insert the account and notify the observers */
1759 insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1761 /* If it's the first remote account then add the connection
1762 specific SMTP servers as well */
1764 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1768 /* This is the callback of the tny_camel_account_set_online called in
1769 on_account_removed to disconnect the account */
1771 on_account_disconnect_when_removing (TnyCamelAccount *account,
1776 ModestTnyAccountStore *self;
1777 ModestTnyAccountStorePrivate *priv;
1779 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1780 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1782 /* Remove the connection-status-changed handler if it's still there */
1783 if (modest_signal_mgr_is_connected (priv->sighandlers,
1785 "connection_status_changed")) {
1786 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1788 "connection_status_changed");
1791 /* Cancel all pending operations */
1792 tny_account_cancel (TNY_ACCOUNT (account));
1794 /* Unref the extra reference added by get_server_account */
1795 g_object_unref (account);
1797 /* Clear the cache if it's an store account */
1798 if (TNY_IS_STORE_ACCOUNT (account)) {
1799 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1800 } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1801 ModestTnySendQueue* send_queue;
1802 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1803 if (TNY_IS_SEND_QUEUE (send_queue)) {
1804 if (modest_tny_send_queue_sending_in_progress (send_queue))
1805 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1806 TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE,
1808 modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1814 * We use this one for both removing "normal" and "connection
1815 * specific" transport accounts
1818 remove_transport_account (ModestTnyAccountStore *self,
1819 TnyTransportAccount *transport_account)
1821 ModestTnyAccountStorePrivate *priv;
1822 TnyFolder *outbox = NULL;
1824 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1826 /* Remove it from the list of accounts and notify the
1827 observers. Do not need to wait for account
1829 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1830 tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1832 /* Remove the OUTBOX of the account from the global outbox */
1833 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1835 if (TNY_IS_FOLDER (outbox)) {
1836 TnyAccount *local_account = NULL;
1837 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1839 if (outbox_account) {
1840 tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1841 /* Remove existing emails to send */
1842 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1843 g_object_unref (outbox_account);
1846 local_account = modest_tny_account_store_get_local_folders_account (self);
1847 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1850 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1852 /* Notify the change in the local account */
1853 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1854 g_object_unref (local_account);
1856 g_warning ("Removing a transport account that has no outbox");
1859 /* Cancel all pending operations */
1860 tny_account_cancel (TNY_ACCOUNT (transport_account));
1862 /* Disconnect and notify the observers. The callback will free the reference */
1863 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1864 on_account_disconnect_when_removing, self);
1868 on_account_removed (ModestAccountMgr *acc_mgr,
1869 const gchar *account,
1872 TnyAccount *store_account = NULL, *transport_account = NULL;
1873 ModestTnyAccountStore *self;
1874 ModestTnyAccountStorePrivate *priv;
1876 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1877 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1879 /* Get the server and the transport account */
1881 modest_tny_account_store_get_server_account (self, account,
1882 TNY_ACCOUNT_TYPE_STORE);
1884 modest_tny_account_store_get_server_account (self, account,
1885 TNY_ACCOUNT_TYPE_TRANSPORT);
1887 /* If there was any problem creating the account, for example,
1888 with the configuration system this could not exist */
1889 if (TNY_IS_STORE_ACCOUNT(store_account)) {
1890 /* Forget any cached password for the account */
1891 forget_password_in_memory (self, tny_account_get_id (store_account));
1893 /* Remove it from the list of accounts and notify the
1894 observers. Do not need to wait for account
1896 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1897 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1899 /* Cancel all pending operations */
1900 tny_account_cancel (TNY_ACCOUNT (store_account));
1902 /* Disconnect before deleting the cache, because the
1903 disconnection will rewrite the cache to the
1905 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1906 on_account_disconnect_when_removing, self);
1908 g_warning ("%s: no store account for account %s\n",
1909 __FUNCTION__, account);
1912 /* If there was any problem creating the account, for example,
1913 with the configuration system this could not exist */
1914 if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1916 /* Forget any cached password for the account */
1917 forget_password_in_memory (self, tny_account_get_id (transport_account));
1919 /* Remove transport account. It'll free the reference
1920 added by get_server_account */
1921 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1923 g_warning ("%s: no transport account for account %s\n",
1924 __FUNCTION__, account);
1927 /* If there are no more user accounts then delete the
1928 transport specific SMTP servers */
1929 if (only_local_accounts (self))
1930 remove_connection_specific_transport_accounts (self);
1933 TnyTransportAccount *
1934 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1937 ModestTnyAccountStorePrivate *priv = NULL;
1938 TnyAccount * tny_account = NULL;
1940 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1942 /* Add the account: */
1944 modest_tny_account_new_from_server_account_name (priv->account_mgr,
1950 g_object_set_data (G_OBJECT(tny_account),
1953 g_object_set_data (G_OBJECT(tny_account),
1954 "connection_specific",
1955 GINT_TO_POINTER (TRUE));
1957 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1958 add_outbox_from_transport_account_to_global_outbox (self,
1963 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1966 return TNY_TRANSPORT_ACCOUNT (tny_account);
1971 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1973 ModestTnyAccountStorePrivate *priv = NULL;
1974 GSList *list_specifics = NULL, *iter = NULL;
1977 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1979 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
1980 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1981 MODEST_CONF_VALUE_STRING, &err);
1984 g_return_if_reached ();
1987 /* Look at each connection-specific transport account for the
1988 * modest account: */
1989 iter = list_specifics;
1991 /* The list alternates between the connection name and the transport name: */
1992 iter = g_slist_next (iter);
1994 const gchar* transport_account_name = (const gchar*) (iter->data);
1995 TnyTransportAccount * account = NULL;
1996 account = modest_tny_account_store_new_connection_specific_transport_account (
1997 self, transport_account_name);
1999 g_object_unref (account);
2001 iter = g_slist_next (iter);
2006 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2008 ModestTnyAccountStorePrivate *priv = NULL;
2009 GSList *list_specifics = NULL, *iter = NULL;
2012 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2015 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2016 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2017 MODEST_CONF_VALUE_STRING, &err);
2020 g_return_if_reached ();
2023 /* Look at each connection-specific transport account for the
2024 * modest account: */
2025 iter = list_specifics;
2027 /* The list alternates between the connection name and the transport name: */
2028 iter = g_slist_next (iter);
2030 const gchar* transport_account_name = (const gchar*) (iter->data);
2031 TnyAccount * account;
2032 account = modest_tny_account_store_get_server_account (self,
2033 transport_account_name,
2034 TNY_ACCOUNT_TYPE_TRANSPORT);
2036 /* the call will free the reference */
2038 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2040 iter = g_slist_next (iter);
2046 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self,
2048 TnyAccount **ac_out)
2050 TnyIterator *acc_iter;
2051 ModestTnyAccountStorePrivate *priv;
2053 TnyAccount *msg_account = NULL;
2055 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2056 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2058 acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2059 while (!msg && !tny_iterator_is_done (acc_iter)) {
2060 TnyList *folders = tny_simple_list_new ();
2061 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2062 TnyIterator *folders_iter = NULL;
2064 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
2065 folders_iter = tny_list_create_iterator (folders);
2067 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2068 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2069 msg = tny_folder_find_msg (folder, uri, NULL);
2072 msg_account = g_object_ref (account);
2074 g_object_unref (folder);
2075 tny_iterator_next (folders_iter);
2077 g_object_unref (folders_iter);
2079 g_object_unref (folders);
2080 g_object_unref (account);
2081 tny_iterator_next (acc_iter);
2084 g_object_unref (acc_iter);
2087 *ac_out = msg_account;
2092 TnyTransportAccount *
2093 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2095 TnyIterator *acc_iter;
2096 ModestTnyAccountStorePrivate *priv;
2097 TnyTransportAccount *header_acc = NULL;
2100 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2101 g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2102 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2103 msg_id = modest_tny_send_queue_get_msg_id (header);
2104 acc_iter = tny_list_create_iterator (priv->transport_accounts);
2105 while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2106 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2107 ModestTnySendQueue *send_queue;
2108 ModestTnySendQueueStatus status;
2109 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2110 if (TNY_IS_SEND_QUEUE (send_queue)) {
2111 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2112 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2113 header_acc = g_object_ref(account);
2115 g_object_unref (account);
2116 tny_iterator_next (acc_iter);
2118 g_object_unref(acc_iter);
2126 modest_tny_account_store_show_account_settings_dialog (ModestTnyAccountStore *self,
2127 const gchar *account_name)
2129 ModestTnyAccountStorePrivate *priv;
2130 gpointer dialog_as_gpointer = NULL;
2133 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2134 found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
2135 account_name, NULL, (gpointer*)&dialog_as_gpointer);
2138 modest_account_settings_dialog_check_allow_changes ((ModestAccountSettingsDialog *) dialog_as_gpointer);
2139 return (GtkWidget *) dialog_as_gpointer;
2141 ModestAccountSettings *settings;
2143 dialog = (GtkWidget *) modest_account_settings_dialog_new ();
2144 settings = modest_account_mgr_load_account_settings (priv->account_mgr, account_name);
2145 modest_account_settings_dialog_set_account (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog), settings);
2146 g_object_unref (settings);
2147 modest_account_settings_dialog_switch_to_user_info (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2148 modest_account_settings_dialog_check_allow_changes (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2149 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
2151 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (account_name), dialog);
2153 g_signal_connect (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide),
2154 g_strdup (account_name));
2156 /* Show it and delete it when it closes: */
2157 g_signal_connect_swapped (dialog,
2159 G_CALLBACK (gtk_widget_destroy),
2161 gtk_widget_show (GTK_WIDGET (dialog));
2169 ModestTnyAccountStore *account_store;
2170 ModestTnyAccountStoreShutdownCallback callback;
2176 account_shutdown_callback (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer userdata)
2178 ShutdownOpData *op_data = (ShutdownOpData *) userdata;
2180 if (op_data->pending == 0) {
2181 if (op_data->callback)
2182 op_data->callback (op_data->account_store, op_data->userdata);
2183 g_object_unref (op_data->account_store);
2189 account_shutdown (TnyAccount *account, ShutdownOpData *op_data)
2191 g_return_if_fail (account && TNY_IS_ACCOUNT(account));
2193 if (TNY_IS_STORE_ACCOUNT (account) &&
2194 !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
2199 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, account_shutdown_callback, op_data);
2204 modest_tny_account_store_shutdown (ModestTnyAccountStore *self,
2205 ModestTnyAccountStoreShutdownCallback callback,
2208 ShutdownOpData *op_data = g_new0 (ShutdownOpData, 1);
2209 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2210 op_data->callback = callback;
2211 op_data->userdata = userdata;
2212 op_data->pending = 0;
2213 op_data->account_store = g_object_ref (self);
2215 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
2216 if (priv->store_accounts) {
2217 tny_list_foreach (priv->store_accounts, (GFunc)account_shutdown, op_data);
2220 if (priv->transport_accounts) {
2221 tny_list_foreach (priv->transport_accounts, (GFunc)account_shutdown, op_data);
2224 if (op_data->pending == 0) {
2225 if (op_data->callback)
2226 op_data->callback (op_data->account_store, op_data->userdata);
2227 g_object_unref (op_data->account_store);