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;
148 /* is sending mail blocked? */
149 gboolean send_mail_blocked;
152 #define MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
153 MODEST_TYPE_TNY_ACCOUNT_STORE, \
154 ModestTnyAccountStorePrivate))
157 static GObjectClass *parent_class = NULL;
159 static guint signals[LAST_SIGNAL] = {0};
162 modest_tny_account_store_get_type (void)
164 static GType my_type = 0;
167 static const GTypeInfo my_info = {
168 sizeof(ModestTnyAccountStoreClass),
169 modest_tny_account_store_base_init, /* base init */
170 NULL, /* base finalize */
171 (GClassInitFunc) modest_tny_account_store_class_init,
172 NULL, /* class finalize */
173 NULL, /* class data */
174 sizeof(ModestTnyAccountStore),
176 (GInstanceInitFunc) modest_tny_account_store_instance_init,
180 static const GInterfaceInfo iface_info = {
181 (GInterfaceInitFunc) modest_tny_account_store_init,
182 NULL, /* interface_finalize */
183 NULL /* interface_data */
186 my_type = g_type_register_static (G_TYPE_OBJECT,
187 "ModestTnyAccountStore",
189 g_type_add_interface_static (my_type, TNY_TYPE_ACCOUNT_STORE,
197 modest_tny_account_store_base_init (gpointer g_class)
199 static gboolean tny_account_store_initialized = FALSE;
201 if (!tny_account_store_initialized) {
203 signals[ACCOUNT_CHANGED_SIGNAL] =
204 g_signal_new ("account_changed",
205 MODEST_TYPE_TNY_ACCOUNT_STORE,
207 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_changed),
209 g_cclosure_marshal_VOID__OBJECT,
210 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
212 signals[ACCOUNT_INSERTED_SIGNAL] =
213 g_signal_new ("account_inserted",
214 MODEST_TYPE_TNY_ACCOUNT_STORE,
216 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_inserted),
218 g_cclosure_marshal_VOID__OBJECT,
219 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
221 signals[ACCOUNT_REMOVED_SIGNAL] =
222 g_signal_new ("account_removed",
223 MODEST_TYPE_TNY_ACCOUNT_STORE,
225 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_removed),
227 g_cclosure_marshal_VOID__OBJECT,
228 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
230 signals[PASSWORD_REQUESTED_SIGNAL] =
231 g_signal_new ("password_requested",
232 MODEST_TYPE_TNY_ACCOUNT_STORE,
234 G_STRUCT_OFFSET(ModestTnyAccountStoreClass, password_requested),
236 modest_marshal_VOID__STRING_POINTER_POINTER_POINTER_POINTER,
237 G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
240 tny_account_store_initialized = TRUE;
246 modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass)
248 GObjectClass *gobject_class;
249 gobject_class = (GObjectClass*) klass;
251 parent_class = g_type_class_peek_parent (klass);
252 gobject_class->finalize = modest_tny_account_store_finalize;
254 g_type_class_add_private (gobject_class,
255 sizeof(ModestTnyAccountStorePrivate));
259 modest_tny_account_store_instance_init (ModestTnyAccountStore *obj)
261 GnomeVFSVolumeMonitor* monitor = NULL;
262 ModestTnyAccountStorePrivate *priv;
264 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
266 priv->cache_dir = NULL;
267 priv->account_mgr = NULL;
268 priv->session = NULL;
270 priv->sighandlers = NULL;
271 priv->send_mail_blocked = FALSE;
273 priv->outbox_of_transport = g_hash_table_new_full (g_direct_hash,
278 /* An in-memory store of passwords,
279 * for passwords that are not remembered in the configuration,
280 * so they need to be asked for from the user once in each session:
282 priv->password_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
285 /* A hash-map of modest account names to dialog pointers,
286 * so we can avoid showing the account settings twice for the same modest account: */
287 priv->account_settings_dialog_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
290 /* Respond to volume mounts and unmounts, such
291 * as the insertion/removal of the memory card: */
292 /* This is a singleton, so it does not need to be unrefed. */
293 monitor = gnome_vfs_get_volume_monitor();
295 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
298 G_CALLBACK(on_vfs_volume_mounted),
300 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
301 G_OBJECT(monitor), "volume-unmounted",
302 G_CALLBACK(on_vfs_volume_unmounted),
306 /* disconnect the list of TnyAccounts */
308 account_disconnect (TnyAccount *account)
310 g_return_if_fail (account && TNY_IS_ACCOUNT(account));
312 if (TNY_IS_STORE_ACCOUNT (account) &&
313 !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
316 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, NULL, NULL);
320 /* disconnect the list of TnyAccounts */
322 account_verify_last_ref (TnyAccount *account, const gchar *str)
326 g_return_if_fail (account && TNY_IS_ACCOUNT(account));
328 txt = g_strdup_printf ("%s: %s", str ? str : "?", tny_account_get_name(account));
329 MODEST_DEBUG_VERIFY_OBJECT_LAST_REF(G_OBJECT(account),txt);
337 foreach_account_append_to_list (gpointer data,
342 list = TNY_LIST (user_data);
343 tny_list_append (list, G_OBJECT (data));
346 /********************************************************************/
347 /* Control the state of the MMC local account */
348 /********************************************************************/
350 /** Only call this if the memory card is really mounted.
353 add_mmc_account(ModestTnyAccountStore *self, gboolean emit_insert_signal)
355 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
356 g_return_if_fail (priv->session);
358 TnyAccount *mmc_account = modest_tny_account_new_for_local_folders (priv->account_mgr,
360 MODEST_MCC1_VOLUMEPATH);
362 /* Add to the list of store accounts */
363 tny_list_append (priv->store_accounts, G_OBJECT (mmc_account));
365 if (emit_insert_signal) {
366 g_signal_emit (G_OBJECT (self),
367 signals [ACCOUNT_INSERTED_SIGNAL],
372 g_object_unref (mmc_account);
376 on_vfs_volume_mounted(GnomeVFSVolumeMonitor *volume_monitor,
377 GnomeVFSVolume *volume,
380 ModestTnyAccountStore *self;
381 ModestTnyAccountStorePrivate *priv;
385 self = MODEST_TNY_ACCOUNT_STORE(user_data);
386 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
388 /* Check whether this was the external MMC1 card: */
389 uri = gnome_vfs_volume_get_activation_uri (volume);
391 if (uri && (!strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI))) {
392 add_mmc_account (self, TRUE /* emit the insert signal. */);
399 on_vfs_volume_unmounted(GnomeVFSVolumeMonitor *volume_monitor,
400 GnomeVFSVolume *volume,
403 ModestTnyAccountStore *self;
404 ModestTnyAccountStorePrivate *priv;
407 self = MODEST_TNY_ACCOUNT_STORE(user_data);
408 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
410 /* Check whether this was the external MMC1 card: */
411 uri = gnome_vfs_volume_get_activation_uri (volume);
412 if (uri && (strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI) == 0)) {
413 TnyAccount *mmc_account = NULL;
414 gboolean found = FALSE;
415 TnyIterator *iter = NULL;
417 iter = tny_list_create_iterator (priv->store_accounts);
418 while (!tny_iterator_is_done (iter) && !found) {
421 account = TNY_ACCOUNT (tny_iterator_get_current (iter));
422 if (modest_tny_account_is_memory_card_account (account)) {
424 mmc_account = g_object_ref (account);
426 g_object_unref (account);
427 tny_iterator_next (iter);
429 g_object_unref (iter);
432 /* Remove from the list */
433 tny_list_remove (priv->store_accounts, G_OBJECT (mmc_account));
435 /* Notify observers */
436 g_signal_emit (G_OBJECT (self),
437 signals [ACCOUNT_REMOVED_SIGNAL],
440 g_object_unref (mmc_account);
442 g_warning ("%s: there was no store account for the unmounted MMC",
450 * forget_password_in_memory
451 * @self: a TnyAccountStore instance
452 * @account: A server account.
454 * Forget any password stored in memory for this account.
455 * For instance, this should be called when the user has changed the password in the account settings.
458 forget_password_in_memory (ModestTnyAccountStore *self,
459 const gchar * server_account_name)
461 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
463 if (server_account_name && priv->password_hash) {
464 g_hash_table_remove (priv->password_hash, server_account_name);
469 on_account_changed (ModestAccountMgr *acc_mgr,
470 const gchar *account_name,
471 TnyAccountType account_type,
474 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
475 ModestTnyAccountStorePrivate *priv;
476 TnyList* account_list;
477 gboolean found = FALSE;
478 TnyIterator *iter = NULL;
480 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
481 account_list = (account_type == TNY_ACCOUNT_TYPE_STORE ?
482 priv->store_accounts :
483 priv->transport_accounts);
485 iter = tny_list_create_iterator (account_list);
486 while (!tny_iterator_is_done (iter) && !found) {
487 TnyAccount *tny_account;
488 tny_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
490 if (!strcmp (tny_account_get_id (tny_account), account_name)) {
492 modest_tny_account_update_from_account (tny_account, get_password, forget_password);
493 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_CHANGED_SIGNAL], 0, tny_account);
495 g_object_unref (tny_account);
497 tny_iterator_next (iter);
501 g_object_unref (iter);
505 on_account_settings_hide (GtkWidget *widget, gpointer user_data)
507 /* This is easier than using a struct for the user_data: */
508 ModestTnyAccountStore *self = modest_runtime_get_account_store();
509 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
511 gchar *account_name = (gchar *) user_data;
513 g_hash_table_remove (priv->account_settings_dialog_hash, account_name);
517 show_password_warning_only (const gchar *msg)
519 ModestWindow *main_window =
520 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE); /* don't create */
522 /* Show an explanatory temporary banner: */
524 modest_platform_information_banner (NULL, NULL, msg);
528 show_wrong_password_dialog (TnyAccount *account)
530 /* This is easier than using a struct for the user_data: */
531 ModestTnyAccountStore *self = modest_runtime_get_account_store();
532 GtkWidget *main_window;
533 GtkWidget *dialog = NULL;
535 main_window = (GtkWidget *) modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
536 FALSE); /* don't create */
538 g_warning ("%s: password was wrong; ignoring because no main window", __FUNCTION__);
542 if (g_object_get_data (G_OBJECT (account), "connection_specific") != NULL) {
543 modest_ui_actions_on_smtp_servers (NULL, NULL);
545 const gchar *modest_account_name;
546 modest_account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
547 dialog = modest_tny_account_store_show_account_settings_dialog (self, modest_account_name);
549 /* Show an explanatory temporary banner: */
550 modest_platform_information_banner (dialog, NULL, _("mcen_ib_username_pw_incorrect"));
553 /* This callback will be called by Tinymail when it needs the password
554 * from the user or the account settings.
555 * It can also call forget_password() before calling this,
556 * so that we clear wrong passwords out of our account settings.
557 * Note that TnyAccount here will be the server account. */
559 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
561 ModestTnyAccountStore *self = NULL;
562 ModestTnyAccountStorePrivate *priv;
563 gchar *username = NULL;
565 gpointer pwd_ptr = NULL;
566 gboolean already_asked = FALSE;
567 const gchar *server_account_name;
570 g_return_val_if_fail (account, NULL);
573 g_debug ("%s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
576 /* Get a reference to myself */
577 self = MODEST_TNY_ACCOUNT_STORE (g_object_get_data (G_OBJECT(account), "account_store"));
578 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
580 /* Ensure that we still have this account. It could happen
581 that a set_online was requested *before* removing an
582 account, and due to tinymail emits the get_password
583 function using a g_idle the account could be actually
584 removed *before* this function was really called */
585 url_string = tny_account_get_url_string (account);
587 TnyAccount *tmp_account;
589 tmp_account = tny_account_store_find_account (TNY_ACCOUNT_STORE (self),
597 g_object_unref (tmp_account);
600 server_account_name = tny_account_get_id (account);
601 if (!server_account_name || !self) {
602 g_warning ("modest: %s: could not retrieve account_store for account %s",
603 __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
610 /* This hash map stores passwords, including passwords that are not stored in gconf. */
611 /* Is it in the hash? if it's already there, it must be wrong... */
612 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
613 * type-punned ptrs...*/
614 already_asked = priv->password_hash &&
615 g_hash_table_lookup_extended (priv->password_hash,
618 (gpointer*)&pwd_ptr);
620 g_debug ("%s: Already asked = %d\n", __FUNCTION__, already_asked);
623 /* If the password is not already there, try ModestConf */
624 if (!already_asked) {
625 pwd = modest_account_mgr_get_server_account_password (priv->account_mgr,
626 server_account_name);
627 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
630 /* If it was already asked, it must have been wrong, so ask again */
631 if (already_asked || !pwd || strlen(pwd) == 0) {
632 /* As per the UI spec, if no password was set in the account settings,
633 * ask for it now. But if the password is wrong in the account settings,
634 * then show a banner and the account settings dialog so it can be corrected:
636 ModestTransportStoreProtocol proto;
637 const gboolean settings_have_password =
638 modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
640 /* Show an error and after that ask for a password */
641 proto = modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (account));
642 if (proto == MODEST_PROTOCOL_TRANSPORT_SMTP) {
643 gchar *username = NULL, *msg = NULL;
644 username = modest_account_mgr_get_server_account_username (priv->account_mgr,
645 server_account_name);
646 if (!username || strlen(username) == 0) {
647 msg = g_strdup_printf (_("emev_ni_ui_smtp_userid_invalid"),
648 tny_account_get_name (account),
649 tny_account_get_hostname (account));
652 password = modest_account_mgr_get_server_account_password (priv->account_mgr,
653 server_account_name);
654 if (!password || strlen(password) == 0)
655 msg = g_strdup_printf (_("emev_ni_ui_smtp_passwd_invalid"),
656 tny_account_get_name (account),
657 tny_account_get_hostname (account));
659 msg = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
660 tny_account_get_hostname (account));
665 modest_platform_run_information_dialog (NULL, msg, TRUE);
672 if (settings_have_password) {
673 /* The password must be wrong, so show the account settings dialog so it can be corrected: */
674 show_wrong_password_dialog (account);
682 /* we don't have it yet. Get the password from the user */
683 const gchar* account_id = tny_account_get_id (account);
684 gboolean remember = FALSE;
689 gboolean username_known =
690 modest_account_mgr_get_server_account_username_has_succeeded(priv->account_mgr,
691 server_account_name);
692 /* If the login has ever succeeded then show a specific message */
694 msg = dgettext ("hildon-common-strings", "ecdg_ib_set_password_incorrect");
696 msg = _("mcen_ib_username_pw_incorrect");
697 show_password_warning_only (msg);
700 /* Request password */
701 g_signal_emit (G_OBJECT (self), signals[PASSWORD_REQUESTED_SIGNAL], 0,
702 account_id, /* server_account_name */
703 &username, &pwd, cancel, &remember);
707 /* The password will be returned as the result,
708 * but we need to tell tinymail about the username too: */
710 /* WARNING: I disabled setting username as this can cause locks. Anyway,
711 * as now we have the password dialog username entry always dimmed
712 * this shouldn't be a problem */
715 /* tny_account_set_user (account, username); */
717 /* Do not save the password in gconf, because
718 * the UI spec says "The password will never
719 * be saved in the account": */
721 /* We need to dup the string even knowing that
722 it's already a dup of the contents of an
723 entry, because it if it's wrong, then camel
725 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
727 g_hash_table_remove (priv->password_hash, server_account_name);
742 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
744 g_return_if_fail (account);
746 ModestTnyAccountStorePrivate *priv;
748 gpointer pwd_ptr = NULL;
749 gboolean already_asked = FALSE;
751 const gchar *server_account_name = tny_account_get_id (account);
753 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
755 /* This hash map stores passwords, including passwords that are not stored in gconf. */
756 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
757 * type-punned ptrs...*/
758 already_asked = priv->password_hash &&
759 g_hash_table_lookup_extended (priv->password_hash,
762 (gpointer*)&pwd_ptr);
765 g_hash_table_remove (priv->password_hash, server_account_name);
773 /* tinymail calls this if the connection failed due to an incorrect password.
774 * And it seems to call this for any general connection failure. */
776 forget_password (TnyAccount *account)
778 ModestTnyAccountStore *self;
779 ModestTnyAccountStorePrivate *priv;
780 const TnyAccountStore *account_store;
784 account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
786 self = MODEST_TNY_ACCOUNT_STORE (account_store);
787 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
788 key = tny_account_get_id (account);
790 /* Do not remove the key, this will allow us to detect that we
791 have already asked for it at least once */
792 pwd = g_hash_table_lookup (priv->password_hash, key);
794 memset (pwd, 0, strlen (pwd));
795 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
798 /* Remove from configuration system */
800 modest_account_mgr_unset (priv->account_mgr,
801 key, MODEST_ACCOUNT_PASSWORD, TRUE);
806 modest_tny_account_store_finalize (GObject *obj)
808 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(obj);
809 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
811 g_free (priv->cache_dir);
812 priv->cache_dir = NULL;
814 if (priv->password_hash) {
815 g_hash_table_destroy (priv->password_hash);
816 priv->password_hash = NULL;
819 if (priv->account_settings_dialog_hash) {
820 g_hash_table_destroy (priv->account_settings_dialog_hash);
821 priv->account_settings_dialog_hash = NULL;
824 if (priv->outbox_of_transport) {
825 g_hash_table_destroy (priv->outbox_of_transport);
826 priv->outbox_of_transport = NULL;
829 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
830 priv->sighandlers = NULL;
832 if (priv->account_mgr) {
833 g_object_unref (G_OBJECT(priv->account_mgr));
834 priv->account_mgr = NULL;
838 g_object_unref (G_OBJECT(priv->device));
842 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
843 if (priv->store_accounts) {
844 tny_list_foreach (priv->store_accounts, (GFunc)account_disconnect, NULL);
845 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
846 g_object_unref (priv->store_accounts);
847 priv->store_accounts = NULL;
850 if (priv->transport_accounts) {
851 tny_list_foreach (priv->transport_accounts, (GFunc)account_disconnect, NULL);
852 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
853 g_object_unref (priv->transport_accounts);
854 priv->transport_accounts = NULL;
857 if (priv->store_accounts_outboxes) {
858 g_object_unref (priv->store_accounts_outboxes);
859 priv->store_accounts_outboxes = NULL;
863 camel_object_unref (CAMEL_OBJECT(priv->session));
864 priv->session = NULL;
869 G_OBJECT_CLASS(parent_class)->finalize (obj);
873 volume_path_is_mounted (const gchar* path)
875 g_return_val_if_fail (path, FALSE);
877 gboolean result = FALSE;
878 gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
879 g_return_val_if_fail (path_as_uri, FALSE);
881 /* Get the monitor singleton: */
882 GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
884 /* This seems like a simpler way to do this, but it returns a
885 * GnomeVFSVolume even if the drive is not mounted: */
887 GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor,
888 MODEST_MCC1_VOLUMEPATH);
890 gnome_vfs_volume_unref(volume);
894 /* Get the mounted volumes from the monitor: */
895 GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
897 for (iter = list; iter; iter = g_list_next (iter)) {
898 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
902 gnome_vfs_volume_get_display_name (volume);
903 printf ("volume display name=%s\n", display_name);
904 g_free (display_name);
908 gnome_vfs_volume_get_activation_uri (volume);
909 /* printf (" uri=%s\n", uri); */
910 if (uri && (strcmp (uri, path_as_uri) == 0))
915 gnome_vfs_volume_unref (volume);
921 g_free (path_as_uri);
926 ModestTnyAccountStore*
927 modest_tny_account_store_new (ModestAccountMgr *account_mgr,
931 ModestTnyAccountStorePrivate *priv;
932 TnyAccount *local_account = NULL;
934 g_return_val_if_fail (account_mgr, NULL);
935 g_return_val_if_fail (device, NULL);
937 obj = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
938 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
940 priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
941 priv->device = g_object_ref (device);
943 priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
944 if (!priv->session) {
945 g_warning ("failed to get TnySessionCamel");
949 /* Set the ui locker */
950 tny_session_camel_set_ui_locker (priv->session, tny_gtk_lockable_new ());
952 /* Connect signals */
953 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
954 G_OBJECT(account_mgr), "account_inserted",
955 G_CALLBACK (on_account_inserted), obj);
956 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
957 G_OBJECT(account_mgr), "account_changed",
958 G_CALLBACK (on_account_changed), obj);
959 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
960 G_OBJECT(account_mgr), "account_removed",
961 G_CALLBACK (on_account_removed), obj);
963 /* Create the lists of accounts */
964 priv->store_accounts = tny_simple_list_new ();
965 priv->transport_accounts = tny_simple_list_new ();
966 priv->store_accounts_outboxes = tny_simple_list_new ();
968 /* Create the local folders account */
970 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
971 tny_list_append (priv->store_accounts, G_OBJECT(local_account));
972 g_object_unref (local_account);
974 /* Add the other remote accounts. Do this after adding the
975 local account, because we need to add our outboxes to the
976 global OUTBOX hosted in the local account */
977 add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
979 /* Add connection-specific transport accounts if there are any
980 accounts available */
981 if (!only_local_accounts (MODEST_TNY_ACCOUNT_STORE(obj)))
982 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
984 /* This is a singleton, so it does not need to be unrefed. */
985 if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
987 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */);
990 return MODEST_TNY_ACCOUNT_STORE(obj);
994 modest_tny_account_store_get_accounts (TnyAccountStore *self,
996 TnyGetAccountsRequestType request_type)
998 ModestTnyAccountStorePrivate *priv;
1000 g_return_if_fail (self);
1001 g_return_if_fail (TNY_IS_LIST(list));
1003 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1005 switch (request_type) {
1006 case TNY_ACCOUNT_STORE_BOTH:
1007 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1008 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1010 case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
1011 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1013 case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
1014 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1017 g_return_if_reached ();
1020 /* Initialize session. Why do we need this ??? */
1021 tny_session_camel_set_initialized (priv->session);
1026 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
1028 ModestTnyAccountStorePrivate *priv;
1029 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1031 if (!priv->cache_dir)
1032 priv->cache_dir = g_build_filename (g_get_home_dir(),
1033 MODEST_DIR, MODEST_CACHE_DIR, NULL);
1034 return priv->cache_dir;
1039 * callers need to unref
1042 modest_tny_account_store_get_device (TnyAccountStore *self)
1044 ModestTnyAccountStorePrivate *priv;
1046 g_return_val_if_fail (self, NULL);
1048 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1051 return g_object_ref (G_OBJECT(priv->device));
1058 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1060 return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self),
1061 MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1068 modest_tny_account_store_alert (TnyAccountStore *self,
1069 TnyAccount *account,
1074 ModestTransportStoreProtocol proto =
1075 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
1076 const gchar* server_name = "";
1077 gchar *prompt = NULL;
1078 gboolean retval = TRUE;
1080 /* NOTE: account may be NULL in some cases */
1081 g_return_val_if_fail (error, FALSE);
1083 /* Get the server name: */
1085 server_name = tny_account_get_hostname (account);
1086 const gchar *proto_name = tny_account_get_proto (account);
1088 proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1090 g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__,
1091 tny_account_get_id (account));
1096 switch (error->code) {
1097 case TNY_SYSTEM_ERROR_CANCEL:
1098 /* Don't show waste the user's time by showing him a dialog telling
1099 * him that he has just cancelled something: */
1102 case TNY_SERVICE_ERROR_PROTOCOL:
1103 /* Like a BAD from IMAP (protocol error) */
1104 case TNY_SERVICE_ERROR_LOST_CONNECTION:
1105 /* Lost the connection with the service */
1106 case TNY_SERVICE_ERROR_UNAVAILABLE:
1107 /* You must be working online for this operation */
1108 case TNY_SERVICE_ERROR_CONNECT:
1110 case MODEST_PROTOCOL_STORE_POP:
1111 prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1114 case MODEST_PROTOCOL_STORE_IMAP:
1115 prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1118 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1119 prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1123 g_return_val_if_reached (FALSE);
1127 case TNY_SERVICE_ERROR_AUTHENTICATE:
1128 /* It seems that there's no better error to show with
1129 * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1130 * may appear if there's a timeout during auth */
1132 case MODEST_PROTOCOL_STORE_POP:
1133 prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1136 case MODEST_PROTOCOL_STORE_IMAP:
1137 prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1140 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1141 prompt = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
1145 g_return_val_if_reached (FALSE);
1149 case TNY_SERVICE_ERROR_CERTIFICATE:
1150 /* We'll show the proper dialog later */
1153 case TNY_SYSTEM_ERROR_MEMORY:
1154 /* Can't allocate memory for this operation */
1156 case TNY_SERVICE_ERROR_UNKNOWN:
1159 g_debug ("Unexpected error %d", error->code);
1160 g_return_val_if_reached (FALSE);
1164 if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1165 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1168 else if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE) {
1169 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1171 /* Show the account dialog if it was wrong */
1172 if (error->code == TNY_SERVICE_ERROR_CONNECT ||
1173 error->code == TNY_SERVICE_ERROR_AUTHENTICATE)
1174 show_wrong_password_dialog (account);
1179 g_debug ("%s: error code %d (%s", __FUNCTION__, error->code, error->message);
1189 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1191 TnyAccountStoreIface *klass;
1193 g_return_if_fail (g);
1195 klass = (TnyAccountStoreIface *)g;
1197 klass->get_accounts =
1198 modest_tny_account_store_get_accounts;
1199 klass->get_cache_dir =
1200 modest_tny_account_store_get_cache_dir;
1202 modest_tny_account_store_get_device;
1204 modest_tny_account_store_alert;
1205 klass->find_account =
1206 modest_tny_account_store_find_account_by_url;
1210 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1211 ModestTnyGetPassFunc func)
1213 /* not implemented, we use signals */
1214 g_printerr ("modest: set_get_pass_func not implemented\n");
1218 modest_tny_account_store_get_session (TnyAccountStore *self)
1220 g_return_val_if_fail (self, NULL);
1221 return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1225 get_tny_account_by (TnyList *accounts,
1226 ModestTnyAccountStoreQueryType type,
1229 TnyIterator *iter = NULL;
1230 gboolean found = FALSE;
1231 TnyAccount *retval = NULL;
1233 g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1235 if (tny_list_get_length(accounts) == 0) {
1236 g_warning ("%s: account list is empty", __FUNCTION__);
1240 iter = tny_list_create_iterator (accounts);
1241 while (!tny_iterator_is_done (iter) && !found) {
1242 TnyAccount *tmp_account = NULL;
1243 const gchar *val = NULL;
1245 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1246 if (!TNY_IS_ACCOUNT(tmp_account)) {
1247 g_warning ("%s: not a valid account", __FUNCTION__);
1253 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1254 val = tny_account_get_id (tmp_account);
1256 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1257 val = tny_account_get_url_string (tmp_account);
1261 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL &&
1262 tny_account_matches_url_string (tmp_account, str)) {
1263 retval = g_object_ref (tmp_account);
1266 if (val && str && strcmp (val, str) == 0) {
1267 retval = g_object_ref (tmp_account);
1271 g_object_unref (tmp_account);
1272 tny_iterator_next (iter);
1274 g_object_unref (iter);
1280 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self,
1281 ModestTnyAccountStoreQueryType type,
1284 TnyAccount *account = NULL;
1285 ModestTnyAccountStorePrivate *priv;
1287 g_return_val_if_fail (self, NULL);
1288 g_return_val_if_fail (str, NULL);
1290 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1292 /* Search in store accounts */
1293 account = get_tny_account_by (priv->store_accounts, type, str);
1295 /* If we already found something, no need to search the transport accounts */
1297 account = get_tny_account_by (priv->transport_accounts, type, str);
1299 /* If we already found something, no need to search the
1300 per-account outbox accounts */
1302 account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1305 /* Warn if nothing was found. This is generally unusual. */
1307 g_warning("%s: Failed to find account with %s=%s\n",
1309 (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",
1313 /* Returns a new reference to the account if found */
1319 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1320 const gchar *account_name,
1321 TnyAccountType type)
1323 ModestTnyAccountStorePrivate *priv = NULL;
1324 TnyAccount *retval = NULL;
1325 TnyList *account_list = NULL;
1326 TnyIterator *iter = NULL;
1329 g_return_val_if_fail (self, NULL);
1330 g_return_val_if_fail (account_name, NULL);
1331 g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE ||
1332 type == TNY_ACCOUNT_TYPE_TRANSPORT,
1335 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1337 account_list = (type == TNY_ACCOUNT_TYPE_STORE) ?
1338 priv->store_accounts :
1339 priv->transport_accounts;
1341 if (!account_list) {
1342 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__,
1343 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1347 /* Look for the server account */
1349 iter = tny_list_create_iterator (account_list);
1350 while (!tny_iterator_is_done (iter) && !found) {
1351 const gchar *modest_acc_name;
1352 TnyAccount *tmp_account;
1354 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1356 modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1358 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1360 retval = g_object_ref (tmp_account);
1362 /* Free and continue */
1363 g_object_unref (tmp_account);
1364 tny_iterator_next (iter);
1366 g_object_unref (iter);
1369 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1370 "Number of server accounts of this type=%d\n", __FUNCTION__,
1371 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1372 account_name, tny_list_get_length (account_list));
1375 /* Returns a new reference */
1380 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1381 ModestTnyAccountStore *self, const gchar *account_name)
1385 g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1386 g_return_val_if_fail (account_name, NULL);
1388 /* Get the current connection: */
1389 device = modest_runtime_get_device ();
1392 g_warning ("%s: could not get device", __FUNCTION__);
1396 if (!tny_device_is_online (device))
1399 #ifdef MODEST_HAVE_CONIC
1400 g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1402 TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);
1403 const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1404 /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1408 ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1412 const gchar *connection_id = con_ic_iap_get_id (connection);
1413 /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1417 /* Get the connection-specific transport acccount, if any: */
1418 ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1420 /* Check if this account has connection-specific SMTP enabled */
1421 if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1425 gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager,
1428 /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1429 if (!server_account_name) {
1430 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1433 TnyAccount* account = modest_tny_account_store_get_tny_account_by (self,
1434 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1435 server_account_name);
1437 /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1438 g_free (server_account_name);
1440 /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1441 g_object_unref (connection);
1445 return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1446 #endif /* MODEST_HAVE_CONIC */
1451 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1452 const gchar *account_name)
1454 g_return_val_if_fail (self, NULL);
1455 g_return_val_if_fail (account_name, NULL);
1457 if (!account_name || !self)
1460 /* Get the connection-specific transport acccount, if any: */
1461 /* Note: This gives us a reference: */
1462 TnyAccount *account =
1463 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1465 /* If there is no connection-specific transport account (the common case),
1466 * just get the regular transport account: */
1468 /* The special local folders don't have transport accounts. */
1469 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1472 /* Note: This gives us a reference: */
1473 account = modest_tny_account_store_get_server_account (self, account_name,
1474 TNY_ACCOUNT_TYPE_TRANSPORT);
1478 /* returns a reference. */
1483 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1485 TnyAccount *account = NULL;
1486 ModestTnyAccountStorePrivate *priv;
1490 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1492 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1495 iter = tny_list_create_iterator (priv->store_accounts);
1496 while (!tny_iterator_is_done (iter) && !found) {
1497 TnyAccount *tmp_account;
1499 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1500 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1501 account = g_object_ref (tmp_account);
1504 g_object_unref (tmp_account);
1505 tny_iterator_next (iter);
1507 g_object_unref (iter);
1509 /* Returns a new reference to the account */
1514 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1516 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1519 return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1520 MODEST_MMC_ACCOUNT_ID);
1524 /*********************************************************************************/
1526 add_existing_accounts (ModestTnyAccountStore *self)
1528 GSList *account_names = NULL, *iter = NULL;
1529 ModestTnyAccountStorePrivate *priv = NULL;
1531 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1533 /* These are account names, not server_account names */
1534 account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1536 for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1537 const gchar *account_name = (const gchar*) iter->data;
1539 /* Insert all enabled accounts without notifying */
1540 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1541 insert_account (self, account_name, FALSE);
1543 modest_account_mgr_free_account_names (account_names);
1547 connection_status_changed (TnyAccount *account,
1548 TnyConnectionStatus status,
1551 /* We do this here and not in the connection policy because we
1552 don't want to do it for every account, just for the
1553 accounts that are interactively added when modest is
1555 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1556 const gchar *account_name;
1557 ModestWindow *main_window;
1558 ModestTnyAccountStorePrivate *priv = NULL;
1560 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1562 /* Remove this handler */
1563 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1565 "connection_status_changed");
1567 /* Perform a send receive */
1568 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1569 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1570 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, TRUE, main_window);
1575 create_tny_account (ModestTnyAccountStore *self,
1577 TnyAccountType type,
1580 TnyAccount *account = NULL;
1581 ModestTnyAccountStorePrivate *priv = NULL;
1583 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1585 account = modest_tny_account_new_from_account (priv->account_mgr,
1592 /* Forget any cached password for the account, so that
1593 we use a new account if any */
1594 forget_password_in_memory (self, tny_account_get_id (account));
1596 /* Install a signal handler that will refresh the
1597 account the first time it becomes online. Do this
1598 only if we're adding a new account while the
1599 program is running (we do not want to do this
1601 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1602 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1604 "connection_status_changed",
1605 G_CALLBACK (connection_status_changed),
1608 /* Set the account store */
1609 g_object_set_data (G_OBJECT(account), "account_store", self);
1611 g_printerr ("modest: failed to create account for %s\n", name);
1617 typedef struct _AddOutboxInfo {
1618 ModestTnyAccountStore *account_store;
1619 TnyAccount *transport_account;
1623 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1629 TnyIterator *iter_folders;
1630 TnyFolder *per_account_outbox;
1631 TnyAccount *local_account = NULL;
1632 AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1633 ModestTnyAccountStorePrivate *priv = NULL;
1634 ModestTnyAccountStore *self;
1636 self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1637 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1639 /* Note that this could happen if there is not enough space
1640 available on disk, then the outbox folder could not be
1642 if (tny_list_get_length (list) != 1) {
1643 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1644 tny_list_get_length (list));
1648 iter_folders = tny_list_create_iterator (list);
1649 per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1650 g_object_unref (iter_folders);
1651 g_object_unref (list);
1653 /* Add the outbox of the new per-account-local-outbox account
1654 to the global local merged OUTBOX of the local folders
1656 local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1657 modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1658 per_account_outbox);
1659 /* Add the pair to the hash table */
1660 g_hash_table_insert (priv->outbox_of_transport,
1661 info->transport_account,
1662 per_account_outbox);
1664 /* Notify that the local account changed */
1665 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1666 g_object_unref (local_account);
1667 g_object_unref (per_account_outbox);
1670 g_object_unref (info->transport_account);
1671 g_slice_free (AddOutboxInfo, info);
1676 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1677 const gchar *account_name,
1678 TnyAccount *transport_account)
1680 TnyList *folders = NULL;
1681 TnyAccount *account_outbox = NULL;
1682 ModestTnyAccountStorePrivate *priv = NULL;
1683 AddOutboxInfo *info;
1685 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1687 /* Create per account local outbox */
1689 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr,
1693 if (!G_IS_OBJECT (account_outbox)) {
1694 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1698 tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1700 /* Get the outbox folder */
1701 folders = tny_simple_list_new ();
1702 info = g_slice_new0 (AddOutboxInfo);
1703 info->account_store = self;
1704 info->transport_account = g_object_ref (transport_account);
1705 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL,
1706 add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1707 g_object_unref (account_outbox);
1711 * This function will be used for both adding new accounts and for the
1712 * initialization. In the initialization we do not want to emit
1713 * signals so notify will be FALSE, in the case of account additions
1714 * we do want to notify the observers
1717 insert_account (ModestTnyAccountStore *self,
1718 const gchar *account,
1721 ModestTnyAccountStorePrivate *priv = NULL;
1722 TnyAccount *store_account = NULL, *transport_account = NULL;
1724 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1726 /* Get the server and the transport account */
1727 store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1728 if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1729 g_warning ("%s: failed to create store account", __FUNCTION__);
1733 transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1734 if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1735 g_warning ("%s: failed to create transport account", __FUNCTION__);
1736 g_object_unref (store_account);
1740 /* Add accounts to the lists */
1741 tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1742 tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1744 /* Create a new pseudo-account with an outbox for this
1745 transport account and add it to the global outbox
1746 in the local account */
1747 add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1749 /* Notify the observers. We do it after everything is
1752 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1753 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1757 g_object_unref (store_account);
1758 g_object_unref (transport_account);
1762 only_local_accounts (ModestTnyAccountStore *self)
1764 ModestTnyAccountStorePrivate *priv = NULL;
1765 gboolean only_local = TRUE;
1768 /* Check if this is the first remote account we add */
1769 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1770 iter = tny_list_create_iterator (priv->store_accounts);
1772 while (!tny_iterator_is_done (iter) && only_local) {
1773 TnyAccount *account = (TnyAccount *) tny_iterator_get_current (iter);
1774 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1776 g_object_unref (account);
1777 tny_iterator_next (iter);
1779 g_object_unref (iter);
1785 on_account_inserted (ModestAccountMgr *acc_mgr,
1786 const gchar *account,
1789 gboolean add_specific;
1791 add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1793 /* Insert the account and notify the observers */
1794 insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1796 /* If it's the first remote account then add the connection
1797 specific SMTP servers as well */
1799 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1803 /* This is the callback of the tny_camel_account_set_online called in
1804 on_account_removed to disconnect the account */
1806 on_account_disconnect_when_removing (TnyCamelAccount *account,
1811 ModestTnyAccountStore *self;
1812 ModestTnyAccountStorePrivate *priv;
1814 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1815 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1817 /* Remove the connection-status-changed handler if it's still there */
1818 if (modest_signal_mgr_is_connected (priv->sighandlers,
1820 "connection_status_changed")) {
1821 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1823 "connection_status_changed");
1826 /* Cancel all pending operations */
1827 tny_account_cancel (TNY_ACCOUNT (account));
1829 /* Unref the extra reference added by get_server_account */
1830 g_object_unref (account);
1832 /* Clear the cache if it's an store account */
1833 if (TNY_IS_STORE_ACCOUNT (account)) {
1834 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1835 } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1836 ModestTnySendQueue* send_queue;
1837 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1838 if (TNY_IS_SEND_QUEUE (send_queue)) {
1839 if (modest_tny_send_queue_sending_in_progress (send_queue))
1840 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1841 TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE,
1843 modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1849 * We use this one for both removing "normal" and "connection
1850 * specific" transport accounts
1853 remove_transport_account (ModestTnyAccountStore *self,
1854 TnyTransportAccount *transport_account)
1856 ModestTnyAccountStorePrivate *priv;
1857 TnyFolder *outbox = NULL;
1859 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1861 /* Remove it from the list of accounts and notify the
1862 observers. Do not need to wait for account
1864 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1865 tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1867 /* Remove the OUTBOX of the account from the global outbox */
1868 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1870 if (TNY_IS_FOLDER (outbox)) {
1871 TnyAccount *local_account = NULL;
1872 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1874 if (outbox_account) {
1875 tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1876 /* Remove existing emails to send */
1877 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1878 g_object_unref (outbox_account);
1881 local_account = modest_tny_account_store_get_local_folders_account (self);
1882 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1885 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1887 /* Notify the change in the local account */
1888 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1889 g_object_unref (local_account);
1891 g_warning ("Removing a transport account that has no outbox");
1894 /* Cancel all pending operations */
1895 tny_account_cancel (TNY_ACCOUNT (transport_account));
1897 /* Disconnect and notify the observers. The callback will free the reference */
1898 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1899 on_account_disconnect_when_removing, self);
1903 on_account_removed (ModestAccountMgr *acc_mgr,
1904 const gchar *account,
1907 TnyAccount *store_account = NULL, *transport_account = NULL;
1908 ModestTnyAccountStore *self;
1909 ModestTnyAccountStorePrivate *priv;
1911 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1912 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1914 /* Get the server and the transport account */
1916 modest_tny_account_store_get_server_account (self, account,
1917 TNY_ACCOUNT_TYPE_STORE);
1919 modest_tny_account_store_get_server_account (self, account,
1920 TNY_ACCOUNT_TYPE_TRANSPORT);
1922 /* If there was any problem creating the account, for example,
1923 with the configuration system this could not exist */
1924 if (TNY_IS_STORE_ACCOUNT(store_account)) {
1925 /* Forget any cached password for the account */
1926 forget_password_in_memory (self, tny_account_get_id (store_account));
1928 /* Remove it from the list of accounts and notify the
1929 observers. Do not need to wait for account
1931 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1932 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1934 /* Cancel all pending operations */
1935 tny_account_cancel (TNY_ACCOUNT (store_account));
1937 /* Disconnect before deleting the cache, because the
1938 disconnection will rewrite the cache to the
1940 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1941 on_account_disconnect_when_removing, self);
1943 g_warning ("%s: no store account for account %s\n",
1944 __FUNCTION__, account);
1947 /* If there was any problem creating the account, for example,
1948 with the configuration system this could not exist */
1949 if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1951 /* Forget any cached password for the account */
1952 forget_password_in_memory (self, tny_account_get_id (transport_account));
1954 /* Remove transport account. It'll free the reference
1955 added by get_server_account */
1956 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1958 g_warning ("%s: no transport account for account %s\n",
1959 __FUNCTION__, account);
1962 /* If there are no more user accounts then delete the
1963 transport specific SMTP servers */
1964 if (only_local_accounts (self))
1965 remove_connection_specific_transport_accounts (self);
1968 TnyTransportAccount *
1969 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1972 ModestTnyAccountStorePrivate *priv = NULL;
1973 TnyAccount * tny_account = NULL;
1975 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1977 /* Add the account: */
1979 modest_tny_account_new_from_server_account_name (priv->account_mgr,
1985 g_object_set_data (G_OBJECT(tny_account),
1988 g_object_set_data (G_OBJECT(tny_account),
1989 "connection_specific",
1990 GINT_TO_POINTER (TRUE));
1992 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1993 add_outbox_from_transport_account_to_global_outbox (self,
1998 g_printerr ("modest: failed to create smtp-specific account for %s\n",
2001 return TNY_TRANSPORT_ACCOUNT (tny_account);
2006 add_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);
2014 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2015 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2016 MODEST_CONF_VALUE_STRING, &err);
2019 g_return_if_reached ();
2022 /* Look at each connection-specific transport account for the
2023 * modest account: */
2024 iter = list_specifics;
2026 /* The list alternates between the connection name and the transport name: */
2027 iter = g_slist_next (iter);
2029 const gchar* transport_account_name = (const gchar*) (iter->data);
2030 TnyTransportAccount * account = NULL;
2031 account = modest_tny_account_store_new_connection_specific_transport_account (
2032 self, transport_account_name);
2034 g_object_unref (account);
2036 iter = g_slist_next (iter);
2041 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2043 ModestTnyAccountStorePrivate *priv = NULL;
2044 GSList *list_specifics = NULL, *iter = NULL;
2047 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2050 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2051 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2052 MODEST_CONF_VALUE_STRING, &err);
2055 g_return_if_reached ();
2058 /* Look at each connection-specific transport account for the
2059 * modest account: */
2060 iter = list_specifics;
2062 /* The list alternates between the connection name and the transport name: */
2063 iter = g_slist_next (iter);
2065 const gchar* transport_account_name = (const gchar*) (iter->data);
2066 TnyAccount * account;
2067 account = modest_tny_account_store_get_server_account (self,
2068 transport_account_name,
2069 TNY_ACCOUNT_TYPE_TRANSPORT);
2071 /* the call will free the reference */
2073 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2075 iter = g_slist_next (iter);
2081 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self,
2083 TnyAccount **ac_out)
2085 TnyIterator *acc_iter;
2086 ModestTnyAccountStorePrivate *priv;
2088 TnyAccount *msg_account = NULL;
2090 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2091 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2093 acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2094 while (!msg && !tny_iterator_is_done (acc_iter)) {
2095 TnyList *folders = tny_simple_list_new ();
2096 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2097 TnyIterator *folders_iter = NULL;
2099 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
2100 folders_iter = tny_list_create_iterator (folders);
2102 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2103 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2104 msg = tny_folder_find_msg (folder, uri, NULL);
2107 msg_account = g_object_ref (account);
2109 g_object_unref (folder);
2110 tny_iterator_next (folders_iter);
2112 g_object_unref (folders_iter);
2114 g_object_unref (folders);
2115 g_object_unref (account);
2116 tny_iterator_next (acc_iter);
2119 g_object_unref (acc_iter);
2122 *ac_out = msg_account;
2127 TnyTransportAccount *
2128 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2130 TnyIterator *acc_iter;
2131 ModestTnyAccountStorePrivate *priv;
2132 TnyTransportAccount *header_acc = NULL;
2135 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2136 g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2137 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2138 msg_id = modest_tny_send_queue_get_msg_id (header);
2139 acc_iter = tny_list_create_iterator (priv->transport_accounts);
2140 while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2141 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2142 ModestTnySendQueue *send_queue;
2143 ModestTnySendQueueStatus status;
2144 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2145 if (TNY_IS_SEND_QUEUE (send_queue)) {
2146 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2147 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2148 header_acc = g_object_ref(account);
2150 g_object_unref (account);
2151 tny_iterator_next (acc_iter);
2153 g_object_unref(acc_iter);
2161 modest_tny_account_store_show_account_settings_dialog (ModestTnyAccountStore *self,
2162 const gchar *account_name)
2164 ModestTnyAccountStorePrivate *priv;
2165 gpointer dialog_as_gpointer = NULL;
2168 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2169 found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
2170 account_name, NULL, (gpointer*)&dialog_as_gpointer);
2173 modest_account_settings_dialog_check_allow_changes ((ModestAccountSettingsDialog *) dialog_as_gpointer);
2174 return (GtkWidget *) dialog_as_gpointer;
2176 ModestAccountSettings *settings;
2178 dialog = (GtkWidget *) modest_account_settings_dialog_new ();
2179 settings = modest_account_mgr_load_account_settings (priv->account_mgr, account_name);
2180 modest_account_settings_dialog_set_account (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog), settings);
2181 g_object_unref (settings);
2182 modest_account_settings_dialog_switch_to_user_info (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2183 modest_account_settings_dialog_check_allow_changes (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2184 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
2186 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (account_name), dialog);
2188 g_signal_connect (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide),
2189 g_strdup (account_name));
2191 /* Show it and delete it when it closes: */
2192 g_signal_connect_swapped (dialog,
2194 G_CALLBACK (gtk_widget_destroy),
2196 gtk_widget_show (GTK_WIDGET (dialog));
2204 modest_tny_account_store_is_send_mail_blocked (ModestTnyAccountStore *self)
2206 ModestTnyAccountStorePrivate *priv;
2208 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2210 return priv->send_mail_blocked;
2214 modest_tny_account_store_set_send_mail_blocked (ModestTnyAccountStore *self,
2217 ModestTnyAccountStorePrivate *priv;
2219 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2221 priv->send_mail_blocked = blocked;