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,
1167 else if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE) {
1168 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1170 /* Show the account dialog if it was wrong */
1171 if (error->code == TNY_SERVICE_ERROR_CONNECT ||
1172 error->code == TNY_SERVICE_ERROR_AUTHENTICATE)
1173 show_wrong_password_dialog (account);
1178 g_debug ("%s: error code %d (%s", __FUNCTION__, error->code, error->message);
1188 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1190 TnyAccountStoreIface *klass;
1192 g_return_if_fail (g);
1194 klass = (TnyAccountStoreIface *)g;
1196 klass->get_accounts =
1197 modest_tny_account_store_get_accounts;
1198 klass->get_cache_dir =
1199 modest_tny_account_store_get_cache_dir;
1201 modest_tny_account_store_get_device;
1203 modest_tny_account_store_alert;
1204 klass->find_account =
1205 modest_tny_account_store_find_account_by_url;
1209 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1210 ModestTnyGetPassFunc func)
1212 /* not implemented, we use signals */
1213 g_printerr ("modest: set_get_pass_func not implemented\n");
1217 modest_tny_account_store_get_session (TnyAccountStore *self)
1219 g_return_val_if_fail (self, NULL);
1220 return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1224 get_tny_account_by (TnyList *accounts,
1225 ModestTnyAccountStoreQueryType type,
1228 TnyIterator *iter = NULL;
1229 gboolean found = FALSE;
1230 TnyAccount *retval = NULL;
1232 g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1234 if (tny_list_get_length(accounts) == 0) {
1235 g_warning ("%s: account list is empty", __FUNCTION__);
1239 iter = tny_list_create_iterator (accounts);
1240 while (!tny_iterator_is_done (iter) && !found) {
1241 TnyAccount *tmp_account = NULL;
1242 const gchar *val = NULL;
1244 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1245 if (!TNY_IS_ACCOUNT(tmp_account)) {
1246 g_warning ("%s: not a valid account", __FUNCTION__);
1252 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1253 val = tny_account_get_id (tmp_account);
1255 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1256 val = tny_account_get_url_string (tmp_account);
1260 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL &&
1261 tny_account_matches_url_string (tmp_account, str)) {
1262 retval = g_object_ref (tmp_account);
1265 if (val && str && strcmp (val, str) == 0) {
1266 retval = g_object_ref (tmp_account);
1270 g_object_unref (tmp_account);
1271 tny_iterator_next (iter);
1273 g_object_unref (iter);
1279 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self,
1280 ModestTnyAccountStoreQueryType type,
1283 TnyAccount *account = NULL;
1284 ModestTnyAccountStorePrivate *priv;
1286 g_return_val_if_fail (self, NULL);
1287 g_return_val_if_fail (str, NULL);
1289 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1291 /* Search in store accounts */
1292 account = get_tny_account_by (priv->store_accounts, type, str);
1294 /* If we already found something, no need to search the transport accounts */
1296 account = get_tny_account_by (priv->transport_accounts, type, str);
1298 /* If we already found something, no need to search the
1299 per-account outbox accounts */
1301 account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1304 /* Warn if nothing was found. This is generally unusual. */
1306 g_warning("%s: Failed to find account with %s=%s\n",
1308 (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",
1312 /* Returns a new reference to the account if found */
1318 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1319 const gchar *account_name,
1320 TnyAccountType type)
1322 ModestTnyAccountStorePrivate *priv = NULL;
1323 TnyAccount *retval = NULL;
1324 TnyList *account_list = NULL;
1325 TnyIterator *iter = NULL;
1328 g_return_val_if_fail (self, NULL);
1329 g_return_val_if_fail (account_name, NULL);
1330 g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE ||
1331 type == TNY_ACCOUNT_TYPE_TRANSPORT,
1334 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1336 account_list = (type == TNY_ACCOUNT_TYPE_STORE) ?
1337 priv->store_accounts :
1338 priv->transport_accounts;
1340 if (!account_list) {
1341 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__,
1342 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1346 /* Look for the server account */
1348 iter = tny_list_create_iterator (account_list);
1349 while (!tny_iterator_is_done (iter) && !found) {
1350 const gchar *modest_acc_name;
1351 TnyAccount *tmp_account;
1353 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1355 modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1357 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1359 retval = g_object_ref (tmp_account);
1361 /* Free and continue */
1362 g_object_unref (tmp_account);
1363 tny_iterator_next (iter);
1365 g_object_unref (iter);
1368 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1369 "Number of server accounts of this type=%d\n", __FUNCTION__,
1370 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1371 account_name, tny_list_get_length (account_list));
1374 /* Returns a new reference */
1379 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1380 ModestTnyAccountStore *self, const gchar *account_name)
1384 g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1385 g_return_val_if_fail (account_name, NULL);
1387 /* Get the current connection: */
1388 device = modest_runtime_get_device ();
1391 g_warning ("%s: could not get device", __FUNCTION__);
1395 if (!tny_device_is_online (device))
1398 #ifdef MODEST_HAVE_CONIC
1399 g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1401 TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);
1402 const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1403 /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1407 ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1411 const gchar *connection_id = con_ic_iap_get_id (connection);
1412 /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1416 /* Get the connection-specific transport acccount, if any: */
1417 ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1419 /* Check if this account has connection-specific SMTP enabled */
1420 if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1424 gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager,
1427 /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1428 if (!server_account_name) {
1429 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1432 TnyAccount* account = modest_tny_account_store_get_tny_account_by (self,
1433 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1434 server_account_name);
1436 /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1437 g_free (server_account_name);
1439 /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1440 g_object_unref (connection);
1444 return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1445 #endif /* MODEST_HAVE_CONIC */
1450 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1451 const gchar *account_name)
1453 g_return_val_if_fail (self, NULL);
1454 g_return_val_if_fail (account_name, NULL);
1456 if (!account_name || !self)
1459 /* Get the connection-specific transport acccount, if any: */
1460 /* Note: This gives us a reference: */
1461 TnyAccount *account =
1462 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1464 /* If there is no connection-specific transport account (the common case),
1465 * just get the regular transport account: */
1467 /* The special local folders don't have transport accounts. */
1468 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1471 /* Note: This gives us a reference: */
1472 account = modest_tny_account_store_get_server_account (self, account_name,
1473 TNY_ACCOUNT_TYPE_TRANSPORT);
1477 /* returns a reference. */
1482 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1484 TnyAccount *account = NULL;
1485 ModestTnyAccountStorePrivate *priv;
1489 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1491 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1494 iter = tny_list_create_iterator (priv->store_accounts);
1495 while (!tny_iterator_is_done (iter) && !found) {
1496 TnyAccount *tmp_account;
1498 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1499 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1500 account = g_object_ref (tmp_account);
1503 g_object_unref (tmp_account);
1504 tny_iterator_next (iter);
1506 g_object_unref (iter);
1508 /* Returns a new reference to the account */
1513 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1515 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1518 return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1519 MODEST_MMC_ACCOUNT_ID);
1523 /*********************************************************************************/
1525 add_existing_accounts (ModestTnyAccountStore *self)
1527 GSList *account_names = NULL, *iter = NULL;
1528 ModestTnyAccountStorePrivate *priv = NULL;
1530 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1532 /* These are account names, not server_account names */
1533 account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1535 for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1536 const gchar *account_name = (const gchar*) iter->data;
1538 /* Insert all enabled accounts without notifying */
1539 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1540 insert_account (self, account_name, FALSE);
1542 modest_account_mgr_free_account_names (account_names);
1546 connection_status_changed (TnyAccount *account,
1547 TnyConnectionStatus status,
1550 /* We do this here and not in the connection policy because we
1551 don't want to do it for every account, just for the
1552 accounts that are interactively added when modest is
1554 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1555 const gchar *account_name;
1556 ModestWindow *main_window;
1557 ModestTnyAccountStorePrivate *priv = NULL;
1559 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1561 /* Remove this handler */
1562 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1564 "connection_status_changed");
1566 /* Perform a send receive */
1567 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1568 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1569 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, TRUE, main_window);
1574 create_tny_account (ModestTnyAccountStore *self,
1576 TnyAccountType type,
1579 TnyAccount *account = NULL;
1580 ModestTnyAccountStorePrivate *priv = NULL;
1582 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1584 account = modest_tny_account_new_from_account (priv->account_mgr,
1591 /* Forget any cached password for the account, so that
1592 we use a new account if any */
1593 forget_password_in_memory (self, tny_account_get_id (account));
1595 /* Install a signal handler that will refresh the
1596 account the first time it becomes online. Do this
1597 only if we're adding a new account while the
1598 program is running (we do not want to do this
1600 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1601 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1603 "connection_status_changed",
1604 G_CALLBACK (connection_status_changed),
1607 /* Set the account store */
1608 g_object_set_data (G_OBJECT(account), "account_store", self);
1610 g_printerr ("modest: failed to create account for %s\n", name);
1616 typedef struct _AddOutboxInfo {
1617 ModestTnyAccountStore *account_store;
1618 TnyAccount *transport_account;
1622 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1628 TnyIterator *iter_folders;
1629 TnyFolder *per_account_outbox;
1630 TnyAccount *local_account = NULL;
1631 AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1632 ModestTnyAccountStorePrivate *priv = NULL;
1633 ModestTnyAccountStore *self;
1635 self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1636 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1638 /* Note that this could happen if there is not enough space
1639 available on disk, then the outbox folder could not be
1641 if (tny_list_get_length (list) != 1) {
1642 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1643 tny_list_get_length (list));
1647 iter_folders = tny_list_create_iterator (list);
1648 per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1649 g_object_unref (iter_folders);
1650 g_object_unref (list);
1652 /* Add the outbox of the new per-account-local-outbox account
1653 to the global local merged OUTBOX of the local folders
1655 local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1656 modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1657 per_account_outbox);
1658 /* Add the pair to the hash table */
1659 g_hash_table_insert (priv->outbox_of_transport,
1660 info->transport_account,
1661 per_account_outbox);
1663 /* Notify that the local account changed */
1664 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1665 g_object_unref (local_account);
1666 g_object_unref (per_account_outbox);
1669 g_object_unref (info->transport_account);
1670 g_slice_free (AddOutboxInfo, info);
1675 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1676 const gchar *account_name,
1677 TnyAccount *transport_account)
1679 TnyList *folders = NULL;
1680 TnyAccount *account_outbox = NULL;
1681 ModestTnyAccountStorePrivate *priv = NULL;
1682 AddOutboxInfo *info;
1684 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1686 /* Create per account local outbox */
1688 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr,
1692 if (!G_IS_OBJECT (account_outbox)) {
1693 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1697 tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1699 /* Get the outbox folder */
1700 folders = tny_simple_list_new ();
1701 info = g_slice_new0 (AddOutboxInfo);
1702 info->account_store = self;
1703 info->transport_account = g_object_ref (transport_account);
1704 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL,
1705 add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1706 g_object_unref (account_outbox);
1710 * This function will be used for both adding new accounts and for the
1711 * initialization. In the initialization we do not want to emit
1712 * signals so notify will be FALSE, in the case of account additions
1713 * we do want to notify the observers
1716 insert_account (ModestTnyAccountStore *self,
1717 const gchar *account,
1720 ModestTnyAccountStorePrivate *priv = NULL;
1721 TnyAccount *store_account = NULL, *transport_account = NULL;
1723 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1725 /* Get the server and the transport account */
1726 store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1727 if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1728 g_warning ("%s: failed to create store account", __FUNCTION__);
1732 transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1733 if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1734 g_warning ("%s: failed to create transport account", __FUNCTION__);
1735 g_object_unref (store_account);
1739 /* Add accounts to the lists */
1740 tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1741 tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1743 /* Create a new pseudo-account with an outbox for this
1744 transport account and add it to the global outbox
1745 in the local account */
1746 add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1748 /* Notify the observers. We do it after everything is
1751 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1752 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1756 g_object_unref (store_account);
1757 g_object_unref (transport_account);
1761 only_local_accounts (ModestTnyAccountStore *self)
1763 ModestTnyAccountStorePrivate *priv = NULL;
1764 gboolean only_local = TRUE;
1767 /* Check if this is the first remote account we add */
1768 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1769 iter = tny_list_create_iterator (priv->store_accounts);
1771 while (!tny_iterator_is_done (iter) && only_local) {
1772 TnyAccount *account = (TnyAccount *) tny_iterator_get_current (iter);
1773 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1775 g_object_unref (account);
1776 tny_iterator_next (iter);
1778 g_object_unref (iter);
1784 on_account_inserted (ModestAccountMgr *acc_mgr,
1785 const gchar *account,
1788 gboolean add_specific;
1790 add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1792 /* Insert the account and notify the observers */
1793 insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1795 /* If it's the first remote account then add the connection
1796 specific SMTP servers as well */
1798 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1802 /* This is the callback of the tny_camel_account_set_online called in
1803 on_account_removed to disconnect the account */
1805 on_account_disconnect_when_removing (TnyCamelAccount *account,
1810 ModestTnyAccountStore *self;
1811 ModestTnyAccountStorePrivate *priv;
1813 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1814 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1816 /* Remove the connection-status-changed handler if it's still there */
1817 if (modest_signal_mgr_is_connected (priv->sighandlers,
1819 "connection_status_changed")) {
1820 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1822 "connection_status_changed");
1825 /* Cancel all pending operations */
1826 tny_account_cancel (TNY_ACCOUNT (account));
1828 /* Unref the extra reference added by get_server_account */
1829 g_object_unref (account);
1831 /* Clear the cache if it's an store account */
1832 if (TNY_IS_STORE_ACCOUNT (account)) {
1833 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1834 } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1835 ModestTnySendQueue* send_queue;
1836 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1837 if (TNY_IS_SEND_QUEUE (send_queue)) {
1838 if (modest_tny_send_queue_sending_in_progress (send_queue))
1839 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1840 TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE,
1842 modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1848 * We use this one for both removing "normal" and "connection
1849 * specific" transport accounts
1852 remove_transport_account (ModestTnyAccountStore *self,
1853 TnyTransportAccount *transport_account)
1855 ModestTnyAccountStorePrivate *priv;
1856 TnyFolder *outbox = NULL;
1858 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1860 /* Remove it from the list of accounts and notify the
1861 observers. Do not need to wait for account
1863 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1864 tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1866 /* Remove the OUTBOX of the account from the global outbox */
1867 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1869 if (TNY_IS_FOLDER (outbox)) {
1870 TnyAccount *local_account = NULL;
1871 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1873 if (outbox_account) {
1874 tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1875 /* Remove existing emails to send */
1876 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1877 g_object_unref (outbox_account);
1880 local_account = modest_tny_account_store_get_local_folders_account (self);
1881 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1884 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1886 /* Notify the change in the local account */
1887 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1888 g_object_unref (local_account);
1890 g_warning ("Removing a transport account that has no outbox");
1893 /* Cancel all pending operations */
1894 tny_account_cancel (TNY_ACCOUNT (transport_account));
1896 /* Disconnect and notify the observers. The callback will free the reference */
1897 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1898 on_account_disconnect_when_removing, self);
1902 on_account_removed (ModestAccountMgr *acc_mgr,
1903 const gchar *account,
1906 TnyAccount *store_account = NULL, *transport_account = NULL;
1907 ModestTnyAccountStore *self;
1908 ModestTnyAccountStorePrivate *priv;
1910 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1911 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1913 /* Get the server and the transport account */
1915 modest_tny_account_store_get_server_account (self, account,
1916 TNY_ACCOUNT_TYPE_STORE);
1918 modest_tny_account_store_get_server_account (self, account,
1919 TNY_ACCOUNT_TYPE_TRANSPORT);
1921 /* If there was any problem creating the account, for example,
1922 with the configuration system this could not exist */
1923 if (TNY_IS_STORE_ACCOUNT(store_account)) {
1924 /* Forget any cached password for the account */
1925 forget_password_in_memory (self, tny_account_get_id (store_account));
1927 /* Remove it from the list of accounts and notify the
1928 observers. Do not need to wait for account
1930 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1931 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1933 /* Cancel all pending operations */
1934 tny_account_cancel (TNY_ACCOUNT (store_account));
1936 /* Disconnect before deleting the cache, because the
1937 disconnection will rewrite the cache to the
1939 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1940 on_account_disconnect_when_removing, self);
1942 g_warning ("%s: no store account for account %s\n",
1943 __FUNCTION__, account);
1946 /* If there was any problem creating the account, for example,
1947 with the configuration system this could not exist */
1948 if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1950 /* Forget any cached password for the account */
1951 forget_password_in_memory (self, tny_account_get_id (transport_account));
1953 /* Remove transport account. It'll free the reference
1954 added by get_server_account */
1955 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1957 g_warning ("%s: no transport account for account %s\n",
1958 __FUNCTION__, account);
1961 /* If there are no more user accounts then delete the
1962 transport specific SMTP servers */
1963 if (only_local_accounts (self))
1964 remove_connection_specific_transport_accounts (self);
1967 TnyTransportAccount *
1968 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1971 ModestTnyAccountStorePrivate *priv = NULL;
1972 TnyAccount * tny_account = NULL;
1974 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1976 /* Add the account: */
1978 modest_tny_account_new_from_server_account_name (priv->account_mgr,
1984 g_object_set_data (G_OBJECT(tny_account),
1987 g_object_set_data (G_OBJECT(tny_account),
1988 "connection_specific",
1989 GINT_TO_POINTER (TRUE));
1991 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1992 add_outbox_from_transport_account_to_global_outbox (self,
1997 g_printerr ("modest: failed to create smtp-specific account for %s\n",
2000 return TNY_TRANSPORT_ACCOUNT (tny_account);
2005 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2007 ModestTnyAccountStorePrivate *priv = NULL;
2008 GSList *list_specifics = NULL, *iter = NULL;
2011 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2013 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2014 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2015 MODEST_CONF_VALUE_STRING, &err);
2018 g_return_if_reached ();
2021 /* Look at each connection-specific transport account for the
2022 * modest account: */
2023 iter = list_specifics;
2025 /* The list alternates between the connection name and the transport name: */
2026 iter = g_slist_next (iter);
2028 const gchar* transport_account_name = (const gchar*) (iter->data);
2029 TnyTransportAccount * account = NULL;
2030 account = modest_tny_account_store_new_connection_specific_transport_account (
2031 self, transport_account_name);
2033 g_object_unref (account);
2035 iter = g_slist_next (iter);
2040 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2042 ModestTnyAccountStorePrivate *priv = NULL;
2043 GSList *list_specifics = NULL, *iter = NULL;
2046 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2049 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2050 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2051 MODEST_CONF_VALUE_STRING, &err);
2054 g_return_if_reached ();
2057 /* Look at each connection-specific transport account for the
2058 * modest account: */
2059 iter = list_specifics;
2061 /* The list alternates between the connection name and the transport name: */
2062 iter = g_slist_next (iter);
2064 const gchar* transport_account_name = (const gchar*) (iter->data);
2065 TnyAccount * account;
2066 account = modest_tny_account_store_get_server_account (self,
2067 transport_account_name,
2068 TNY_ACCOUNT_TYPE_TRANSPORT);
2070 /* the call will free the reference */
2072 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2074 iter = g_slist_next (iter);
2080 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self,
2082 TnyAccount **ac_out)
2084 TnyIterator *acc_iter;
2085 ModestTnyAccountStorePrivate *priv;
2087 TnyAccount *msg_account = NULL;
2089 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2090 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2092 acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2093 while (!msg && !tny_iterator_is_done (acc_iter)) {
2094 TnyList *folders = tny_simple_list_new ();
2095 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2096 TnyIterator *folders_iter = NULL;
2098 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
2099 folders_iter = tny_list_create_iterator (folders);
2101 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2102 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2103 msg = tny_folder_find_msg (folder, uri, NULL);
2106 msg_account = g_object_ref (account);
2108 g_object_unref (folder);
2109 tny_iterator_next (folders_iter);
2111 g_object_unref (folders_iter);
2113 g_object_unref (folders);
2114 g_object_unref (account);
2115 tny_iterator_next (acc_iter);
2118 g_object_unref (acc_iter);
2121 *ac_out = msg_account;
2126 TnyTransportAccount *
2127 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2129 TnyIterator *acc_iter;
2130 ModestTnyAccountStorePrivate *priv;
2131 TnyTransportAccount *header_acc = NULL;
2134 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2135 g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2136 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2137 msg_id = modest_tny_send_queue_get_msg_id (header);
2138 acc_iter = tny_list_create_iterator (priv->transport_accounts);
2139 while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2140 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2141 ModestTnySendQueue *send_queue;
2142 ModestTnySendQueueStatus status;
2143 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2144 if (TNY_IS_SEND_QUEUE (send_queue)) {
2145 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2146 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2147 header_acc = g_object_ref(account);
2149 g_object_unref (account);
2150 tny_iterator_next (acc_iter);
2152 g_object_unref(acc_iter);
2160 modest_tny_account_store_show_account_settings_dialog (ModestTnyAccountStore *self,
2161 const gchar *account_name)
2163 ModestTnyAccountStorePrivate *priv;
2164 gpointer dialog_as_gpointer = NULL;
2167 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2168 found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
2169 account_name, NULL, (gpointer*)&dialog_as_gpointer);
2172 modest_account_settings_dialog_check_allow_changes ((ModestAccountSettingsDialog *) dialog_as_gpointer);
2173 return (GtkWidget *) dialog_as_gpointer;
2175 ModestAccountSettings *settings;
2177 dialog = (GtkWidget *) modest_account_settings_dialog_new ();
2178 settings = modest_account_mgr_load_account_settings (priv->account_mgr, account_name);
2179 modest_account_settings_dialog_set_account (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog), settings);
2180 g_object_unref (settings);
2181 modest_account_settings_dialog_switch_to_user_info (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2182 modest_account_settings_dialog_check_allow_changes (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2183 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
2185 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (account_name), dialog);
2187 g_signal_connect (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide),
2188 g_strdup (account_name));
2190 /* Show it and delete it when it closes: */
2191 g_signal_connect_swapped (dialog,
2193 G_CALLBACK (gtk_widget_destroy),
2195 gtk_widget_show (GTK_WIDGET (dialog));
2203 modest_tny_account_store_is_send_mail_blocked (ModestTnyAccountStore *self)
2205 ModestTnyAccountStorePrivate *priv;
2207 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2209 return priv->send_mail_blocked;
2213 modest_tny_account_store_set_send_mail_blocked (ModestTnyAccountStore *self,
2216 ModestTnyAccountStorePrivate *priv;
2218 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2220 priv->send_mail_blocked = blocked;