1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <glib/gi18n.h>
33 #include <tny-error.h>
34 #include <tny-account.h>
35 #include <tny-account-store.h>
36 #include <tny-store-account.h>
37 #include <tny-transport-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-account-store.h>
40 #include <tny-camel-transport-account.h>
41 #include <tny-camel-imap-store-account.h>
42 #include <tny-camel-pop-store-account.h>
44 #include <modest-runtime.h>
45 #include <modest-marshal.h>
46 #include <modest-protocol-info.h>
47 #include <modest-local-folder-info.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-local-folders-account.h>
50 #include <modest-account-mgr.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <widgets/modest-window-mgr.h>
53 #include <modest-signal-mgr.h>
54 #include <modest-debug.h>
56 #include "modest-tny-account-store.h"
57 #include "modest-tny-platform-factory.h"
58 #include <tny-gtk-lockable.h>
59 #include <camel/camel.h>
60 #include <modest-platform.h>
61 #include "modest-ui-actions.h"
62 #include <widgets/modest-account-settings-dialog.h>
64 #ifdef MODEST_PLATFORM_MAEMO
65 #include <tny-maemo-conic-device.h>
66 #include <maemo/modest-maemo-utils.h>
69 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
71 /* 'private'/'protected' functions */
72 static void modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass);
73 static void modest_tny_account_store_finalize (GObject *obj);
74 static void modest_tny_account_store_instance_init (ModestTnyAccountStore *obj);
75 static void modest_tny_account_store_init (gpointer g, gpointer iface_data);
76 static void modest_tny_account_store_base_init (gpointer g_class);
78 static void on_account_inserted (ModestAccountMgr *acc_mgr,
82 static void add_existing_accounts (ModestTnyAccountStore *self);
84 static void insert_account (ModestTnyAccountStore *self,
88 static void on_account_removed (ModestAccountMgr *acc_mgr,
92 static gchar* get_password (TnyAccount *account,
93 const gchar * prompt_not_used,
96 static void forget_password (TnyAccount *account);
98 static void on_vfs_volume_mounted (GnomeVFSVolumeMonitor *volume_monitor,
99 GnomeVFSVolume *volume,
102 static void on_vfs_volume_unmounted (GnomeVFSVolumeMonitor *volume_monitor,
103 GnomeVFSVolume *volume,
106 static void forget_password_in_memory (ModestTnyAccountStore *self,
107 const gchar *server_account_name);
109 static void add_connection_specific_transport_accounts (ModestTnyAccountStore *self);
111 static void remove_connection_specific_transport_accounts (ModestTnyAccountStore *self);
113 static void connection_status_changed (TnyAccount *account,
114 TnyConnectionStatus status,
117 static gboolean only_local_accounts (ModestTnyAccountStore *self);
119 /* list my signals */
121 ACCOUNT_CHANGED_SIGNAL,
122 ACCOUNT_INSERTED_SIGNAL,
123 ACCOUNT_REMOVED_SIGNAL,
125 PASSWORD_REQUESTED_SIGNAL,
129 typedef struct _ModestTnyAccountStorePrivate ModestTnyAccountStorePrivate;
130 struct _ModestTnyAccountStorePrivate {
132 GHashTable *password_hash;
133 GHashTable *account_settings_dialog_hash;
134 ModestAccountMgr *account_mgr;
135 TnySessionCamel *session;
140 /* We cache the lists of accounts here */
141 TnyList *store_accounts;
142 TnyList *transport_accounts;
143 TnyList *store_accounts_outboxes;
145 /* Matches transport accounts and outbox folder */
146 GHashTable *outbox_of_transport;
149 #define MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
150 MODEST_TYPE_TNY_ACCOUNT_STORE, \
151 ModestTnyAccountStorePrivate))
154 static GObjectClass *parent_class = NULL;
156 static guint signals[LAST_SIGNAL] = {0};
159 modest_tny_account_store_get_type (void)
161 static GType my_type = 0;
164 static const GTypeInfo my_info = {
165 sizeof(ModestTnyAccountStoreClass),
166 modest_tny_account_store_base_init, /* base init */
167 NULL, /* base finalize */
168 (GClassInitFunc) modest_tny_account_store_class_init,
169 NULL, /* class finalize */
170 NULL, /* class data */
171 sizeof(ModestTnyAccountStore),
173 (GInstanceInitFunc) modest_tny_account_store_instance_init,
177 static const GInterfaceInfo iface_info = {
178 (GInterfaceInitFunc) modest_tny_account_store_init,
179 NULL, /* interface_finalize */
180 NULL /* interface_data */
183 my_type = g_type_register_static (G_TYPE_OBJECT,
184 "ModestTnyAccountStore",
186 g_type_add_interface_static (my_type, TNY_TYPE_ACCOUNT_STORE,
194 modest_tny_account_store_base_init (gpointer g_class)
196 static gboolean tny_account_store_initialized = FALSE;
198 if (!tny_account_store_initialized) {
200 signals[ACCOUNT_CHANGED_SIGNAL] =
201 g_signal_new ("account_changed",
202 MODEST_TYPE_TNY_ACCOUNT_STORE,
204 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_changed),
206 g_cclosure_marshal_VOID__OBJECT,
207 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
209 signals[ACCOUNT_INSERTED_SIGNAL] =
210 g_signal_new ("account_inserted",
211 MODEST_TYPE_TNY_ACCOUNT_STORE,
213 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_inserted),
215 g_cclosure_marshal_VOID__OBJECT,
216 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
218 signals[ACCOUNT_REMOVED_SIGNAL] =
219 g_signal_new ("account_removed",
220 MODEST_TYPE_TNY_ACCOUNT_STORE,
222 G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_removed),
224 g_cclosure_marshal_VOID__OBJECT,
225 G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
227 signals[PASSWORD_REQUESTED_SIGNAL] =
228 g_signal_new ("password_requested",
229 MODEST_TYPE_TNY_ACCOUNT_STORE,
231 G_STRUCT_OFFSET(ModestTnyAccountStoreClass, password_requested),
233 modest_marshal_VOID__STRING_POINTER_POINTER_POINTER_POINTER,
234 G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
237 tny_account_store_initialized = TRUE;
243 modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass)
245 GObjectClass *gobject_class;
246 gobject_class = (GObjectClass*) klass;
248 parent_class = g_type_class_peek_parent (klass);
249 gobject_class->finalize = modest_tny_account_store_finalize;
251 g_type_class_add_private (gobject_class,
252 sizeof(ModestTnyAccountStorePrivate));
256 modest_tny_account_store_instance_init (ModestTnyAccountStore *obj)
258 GnomeVFSVolumeMonitor* monitor = NULL;
259 ModestTnyAccountStorePrivate *priv;
261 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
263 priv->cache_dir = NULL;
264 priv->account_mgr = NULL;
265 priv->session = NULL;
267 priv->sighandlers = NULL;
269 priv->outbox_of_transport = g_hash_table_new_full (g_direct_hash,
274 /* An in-memory store of passwords,
275 * for passwords that are not remembered in the configuration,
276 * so they need to be asked for from the user once in each session:
278 priv->password_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
281 /* A hash-map of modest account names to dialog pointers,
282 * so we can avoid showing the account settings twice for the same modest account: */
283 priv->account_settings_dialog_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
286 /* Respond to volume mounts and unmounts, such
287 * as the insertion/removal of the memory card: */
288 /* This is a singleton, so it does not need to be unrefed. */
289 monitor = gnome_vfs_get_volume_monitor();
291 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
294 G_CALLBACK(on_vfs_volume_mounted),
296 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
297 G_OBJECT(monitor), "volume-unmounted",
298 G_CALLBACK(on_vfs_volume_unmounted),
302 /* disconnect the list of TnyAccounts */
304 account_verify_last_ref (TnyAccount *account, const gchar *str)
308 g_return_if_fail (account && TNY_IS_ACCOUNT(account));
310 txt = g_strdup_printf ("%s: %s", str ? str : "?", tny_account_get_name(account));
311 MODEST_DEBUG_VERIFY_OBJECT_LAST_REF(G_OBJECT(account),txt);
319 foreach_account_append_to_list (gpointer data,
324 list = TNY_LIST (user_data);
325 tny_list_append (list, G_OBJECT (data));
328 /********************************************************************/
329 /* Control the state of the MMC local account */
330 /********************************************************************/
332 /** Only call this if the memory card is really mounted.
335 add_mmc_account(ModestTnyAccountStore *self, gboolean emit_insert_signal)
337 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
338 g_return_if_fail (priv->session);
340 TnyAccount *mmc_account = modest_tny_account_new_for_local_folders (priv->account_mgr,
342 MODEST_MCC1_VOLUMEPATH);
344 /* Add to the list of store accounts */
345 tny_list_append (priv->store_accounts, G_OBJECT (mmc_account));
347 if (emit_insert_signal) {
348 g_signal_emit (G_OBJECT (self),
349 signals [ACCOUNT_INSERTED_SIGNAL],
354 g_object_unref (mmc_account);
358 on_vfs_volume_mounted(GnomeVFSVolumeMonitor *volume_monitor,
359 GnomeVFSVolume *volume,
362 ModestTnyAccountStore *self;
363 ModestTnyAccountStorePrivate *priv;
367 self = MODEST_TNY_ACCOUNT_STORE(user_data);
368 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
370 /* Check whether this was the external MMC1 card: */
371 uri = gnome_vfs_volume_get_activation_uri (volume);
373 if (uri && (!strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI))) {
374 add_mmc_account (self, TRUE /* emit the insert signal. */);
381 on_vfs_volume_unmounted(GnomeVFSVolumeMonitor *volume_monitor,
382 GnomeVFSVolume *volume,
385 ModestTnyAccountStore *self;
386 ModestTnyAccountStorePrivate *priv;
389 self = MODEST_TNY_ACCOUNT_STORE(user_data);
390 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
392 /* Check whether this was the external MMC1 card: */
393 uri = gnome_vfs_volume_get_activation_uri (volume);
394 if (uri && (strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI) == 0)) {
395 TnyAccount *mmc_account = NULL;
396 gboolean found = FALSE;
397 TnyIterator *iter = NULL;
399 iter = tny_list_create_iterator (priv->store_accounts);
400 while (!tny_iterator_is_done (iter) && !found) {
403 account = TNY_ACCOUNT (tny_iterator_get_current (iter));
404 if (modest_tny_account_is_memory_card_account (account)) {
406 mmc_account = g_object_ref (account);
408 g_object_unref (account);
409 tny_iterator_next (iter);
411 g_object_unref (iter);
414 /* Remove from the list */
415 tny_list_remove (priv->store_accounts, G_OBJECT (mmc_account));
417 /* Notify observers */
418 g_signal_emit (G_OBJECT (self),
419 signals [ACCOUNT_REMOVED_SIGNAL],
422 g_object_unref (mmc_account);
424 g_warning ("%s: there was no store account for the unmounted MMC",
432 * forget_password_in_memory
433 * @self: a TnyAccountStore instance
434 * @account: A server account.
436 * Forget any password stored in memory for this account.
437 * For instance, this should be called when the user has changed the password in the account settings.
440 forget_password_in_memory (ModestTnyAccountStore *self,
441 const gchar * server_account_name)
443 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
445 if (server_account_name && priv->password_hash) {
446 g_hash_table_remove (priv->password_hash, server_account_name);
451 on_account_changed (ModestAccountMgr *acc_mgr,
452 const gchar *account_name,
453 TnyAccountType account_type,
456 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
457 ModestTnyAccountStorePrivate *priv;
458 TnyList* account_list;
459 gboolean found = FALSE;
460 TnyIterator *iter = NULL;
462 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
463 account_list = (account_type == TNY_ACCOUNT_TYPE_STORE ?
464 priv->store_accounts :
465 priv->transport_accounts);
467 iter = tny_list_create_iterator (account_list);
468 while (!tny_iterator_is_done (iter) && !found) {
469 TnyAccount *tny_account;
470 tny_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
472 if (!strcmp (tny_account_get_id (tny_account), account_name)) {
474 modest_tny_account_update_from_account (tny_account, get_password, forget_password);
475 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_CHANGED_SIGNAL], 0, tny_account);
477 g_object_unref (tny_account);
479 tny_iterator_next (iter);
483 g_object_unref (iter);
487 on_account_settings_hide (GtkWidget *widget, gpointer user_data)
489 /* This is easier than using a struct for the user_data: */
490 ModestTnyAccountStore *self = modest_runtime_get_account_store();
491 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
493 gchar *account_name = (gchar *) user_data;
495 g_hash_table_remove (priv->account_settings_dialog_hash, account_name);
499 show_password_warning_only (const gchar *msg)
501 ModestWindow *main_window =
502 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE); /* don't create */
504 /* Show an explanatory temporary banner: */
506 modest_platform_information_banner (NULL, NULL, msg);
510 show_wrong_password_dialog (TnyAccount *account)
512 /* This is easier than using a struct for the user_data: */
513 ModestTnyAccountStore *self = modest_runtime_get_account_store();
514 GtkWidget *main_window;
515 GtkWidget *dialog = NULL;
517 main_window = (GtkWidget *) modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
518 FALSE); /* don't create */
520 g_warning ("%s: password was wrong; ignoring because no main window", __FUNCTION__);
524 if (g_object_get_data (G_OBJECT (account), "connection_specific") != NULL) {
525 modest_ui_actions_on_smtp_servers (NULL, NULL);
527 const gchar *modest_account_name;
528 modest_account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
529 dialog = modest_tny_account_store_show_account_settings_dialog (self, modest_account_name);
530 modest_account_settings_dialog_save_password (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
532 /* Show an explanatory temporary banner: */
533 modest_platform_information_banner (dialog, NULL, _("mcen_ib_username_pw_incorrect"));
536 /* This callback will be called by Tinymail when it needs the password
537 * from the user or the account settings.
538 * It can also call forget_password() before calling this,
539 * so that we clear wrong passwords out of our account settings.
540 * Note that TnyAccount here will be the server account. */
542 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
544 ModestTnyAccountStore *self = NULL;
545 ModestTnyAccountStorePrivate *priv;
546 gchar *username = NULL;
548 gpointer pwd_ptr = NULL;
549 gboolean already_asked = FALSE;
550 const gchar *server_account_name;
553 g_return_val_if_fail (account, NULL);
556 g_debug ("%s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
559 /* Get a reference to myself */
560 self = MODEST_TNY_ACCOUNT_STORE (g_object_get_data (G_OBJECT(account), "account_store"));
561 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
563 /* Ensure that we still have this account. It could happen
564 that a set_online was requested *before* removing an
565 account, and due to tinymail emits the get_password
566 function using a g_idle the account could be actually
567 removed *before* this function was really called */
568 url_string = tny_account_get_url_string (account);
570 TnyAccount *tmp_account;
572 tmp_account = tny_account_store_find_account (TNY_ACCOUNT_STORE (self),
580 g_object_unref (tmp_account);
583 server_account_name = tny_account_get_id (account);
584 if (!server_account_name || !self) {
585 g_warning ("modest: %s: could not retrieve account_store for account %s",
586 __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
593 /* This hash map stores passwords, including passwords that are not stored in gconf. */
594 /* Is it in the hash? if it's already there, it must be wrong... */
595 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
596 * type-punned ptrs...*/
597 already_asked = priv->password_hash &&
598 g_hash_table_lookup_extended (priv->password_hash,
601 (gpointer*)&pwd_ptr);
603 g_debug ("%s: Already asked = %d\n", __FUNCTION__, already_asked);
606 /* If the password is not already there, try ModestConf */
607 if (!already_asked) {
608 pwd = modest_account_mgr_get_server_account_password (priv->account_mgr,
609 server_account_name);
610 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
613 /* If it was already asked, it must have been wrong, so ask again */
614 if (already_asked || !pwd || strlen(pwd) == 0) {
615 /* As per the UI spec, if no password was set in the account settings,
616 * ask for it now. But if the password is wrong in the account settings,
617 * then show a banner and the account settings dialog so it can be corrected:
619 ModestTransportStoreProtocol proto;
620 const gboolean settings_have_password =
621 modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
623 /* Show an error and after that ask for a password */
624 proto = modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (account));
625 if (proto == MODEST_PROTOCOL_TRANSPORT_SMTP) {
626 gchar *username = NULL, *msg = NULL;
627 username = modest_account_mgr_get_server_account_username (priv->account_mgr,
628 server_account_name);
629 if (!username || strlen(username) == 0) {
630 msg = g_strdup_printf (_("emev_ni_ui_smtp_userid_invalid"),
631 tny_account_get_name (account),
632 tny_account_get_hostname (account));
635 password = modest_account_mgr_get_server_account_password (priv->account_mgr,
636 server_account_name);
637 if (!password || strlen(password) == 0)
638 msg = g_strdup_printf (_("emev_ni_ui_smtp_passwd_invalid"),
639 tny_account_get_name (account),
640 tny_account_get_hostname (account));
642 msg = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
643 tny_account_get_hostname (account));
648 modest_platform_run_information_dialog (NULL, msg, TRUE);
655 if (settings_have_password) {
656 /* The password must be wrong, so show the account settings dialog so it can be corrected: */
657 show_wrong_password_dialog (account);
665 /* we don't have it yet. Get the password from the user */
666 const gchar* account_id = tny_account_get_id (account);
667 gboolean remember = FALSE;
672 gboolean username_known =
673 modest_account_mgr_get_server_account_username_has_succeeded(priv->account_mgr,
674 server_account_name);
675 /* If the login has ever succeeded then show a specific message */
677 msg = dgettext ("hildon-common-strings", "ecdg_ib_set_password_incorrect");
679 msg = _("mcen_ib_username_pw_incorrect");
680 show_password_warning_only (msg);
683 /* Request password */
684 g_signal_emit (G_OBJECT (self), signals[PASSWORD_REQUESTED_SIGNAL], 0,
685 account_id, /* server_account_name */
686 &username, &pwd, cancel, &remember);
690 /* The password will be returned as the result,
691 * but we need to tell tinymail about the username too: */
693 /* WARNING: I disabled setting username as this can cause locks. Anyway,
694 * as now we have the password dialog username entry always dimmed
695 * this shouldn't be a problem */
698 /* tny_account_set_user (account, username); */
700 /* Do not save the password in gconf, because
701 * the UI spec says "The password will never
702 * be saved in the account": */
704 /* We need to dup the string even knowing that
705 it's already a dup of the contents of an
706 entry, because it if it's wrong, then camel
708 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
710 g_hash_table_remove (priv->password_hash, server_account_name);
725 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
727 g_return_if_fail (account);
729 ModestTnyAccountStorePrivate *priv;
731 gpointer pwd_ptr = NULL;
732 gboolean already_asked = FALSE;
734 const gchar *server_account_name = tny_account_get_id (account);
736 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
738 /* This hash map stores passwords, including passwords that are not stored in gconf. */
739 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
740 * type-punned ptrs...*/
741 already_asked = priv->password_hash &&
742 g_hash_table_lookup_extended (priv->password_hash,
745 (gpointer*)&pwd_ptr);
748 g_hash_table_remove (priv->password_hash, server_account_name);
756 /* tinymail calls this if the connection failed due to an incorrect password.
757 * And it seems to call this for any general connection failure. */
759 forget_password (TnyAccount *account)
761 ModestTnyAccountStore *self;
762 ModestTnyAccountStorePrivate *priv;
763 const TnyAccountStore *account_store;
767 account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
769 self = MODEST_TNY_ACCOUNT_STORE (account_store);
770 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
771 key = tny_account_get_id (account);
773 /* Do not remove the key, this will allow us to detect that we
774 have already asked for it at least once */
775 pwd = g_hash_table_lookup (priv->password_hash, key);
777 memset (pwd, 0, strlen (pwd));
778 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
781 /* Remove from configuration system */
783 modest_account_mgr_unset (priv->account_mgr,
784 key, MODEST_ACCOUNT_PASSWORD, TRUE);
789 modest_tny_account_store_finalize (GObject *obj)
791 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(obj);
792 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
794 g_free (priv->cache_dir);
795 priv->cache_dir = NULL;
797 if (priv->password_hash) {
798 g_hash_table_destroy (priv->password_hash);
799 priv->password_hash = NULL;
802 if (priv->account_settings_dialog_hash) {
803 g_hash_table_destroy (priv->account_settings_dialog_hash);
804 priv->account_settings_dialog_hash = NULL;
807 if (priv->outbox_of_transport) {
808 g_hash_table_destroy (priv->outbox_of_transport);
809 priv->outbox_of_transport = NULL;
812 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
813 priv->sighandlers = NULL;
815 if (priv->account_mgr) {
816 g_object_unref (G_OBJECT(priv->account_mgr));
817 priv->account_mgr = NULL;
821 g_object_unref (G_OBJECT(priv->device));
825 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
826 if (priv->store_accounts) {
827 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
828 g_object_unref (priv->store_accounts);
829 priv->store_accounts = NULL;
832 if (priv->transport_accounts) {
833 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
834 g_object_unref (priv->transport_accounts);
835 priv->transport_accounts = NULL;
838 if (priv->store_accounts_outboxes) {
839 g_object_unref (priv->store_accounts_outboxes);
840 priv->store_accounts_outboxes = NULL;
844 camel_object_unref (CAMEL_OBJECT(priv->session));
845 priv->session = NULL;
850 G_OBJECT_CLASS(parent_class)->finalize (obj);
854 volume_path_is_mounted (const gchar* path)
856 g_return_val_if_fail (path, FALSE);
858 gboolean result = FALSE;
859 gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
860 g_return_val_if_fail (path_as_uri, FALSE);
862 /* Get the monitor singleton: */
863 GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
865 /* This seems like a simpler way to do this, but it returns a
866 * GnomeVFSVolume even if the drive is not mounted: */
868 GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor,
869 MODEST_MCC1_VOLUMEPATH);
871 gnome_vfs_volume_unref(volume);
875 /* Get the mounted volumes from the monitor: */
876 GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
878 for (iter = list; iter; iter = g_list_next (iter)) {
879 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
883 gnome_vfs_volume_get_display_name (volume);
884 printf ("volume display name=%s\n", display_name);
885 g_free (display_name);
889 gnome_vfs_volume_get_activation_uri (volume);
890 /* printf (" uri=%s\n", uri); */
891 if (uri && (strcmp (uri, path_as_uri) == 0))
896 gnome_vfs_volume_unref (volume);
902 g_free (path_as_uri);
907 ModestTnyAccountStore*
908 modest_tny_account_store_new (ModestAccountMgr *account_mgr,
912 ModestTnyAccountStorePrivate *priv;
913 TnyAccount *local_account = NULL;
915 g_return_val_if_fail (account_mgr, NULL);
916 g_return_val_if_fail (device, NULL);
918 obj = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
919 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
921 priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
922 priv->device = g_object_ref (device);
924 priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
925 if (!priv->session) {
926 g_warning ("failed to get TnySessionCamel");
930 /* Set the ui locker */
931 tny_session_camel_set_ui_locker (priv->session, tny_gtk_lockable_new ());
933 /* Connect signals */
934 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
935 G_OBJECT(account_mgr), "account_inserted",
936 G_CALLBACK (on_account_inserted), obj);
937 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
938 G_OBJECT(account_mgr), "account_changed",
939 G_CALLBACK (on_account_changed), obj);
940 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
941 G_OBJECT(account_mgr), "account_removed",
942 G_CALLBACK (on_account_removed), obj);
944 /* Create the lists of accounts */
945 priv->store_accounts = tny_simple_list_new ();
946 priv->transport_accounts = tny_simple_list_new ();
947 priv->store_accounts_outboxes = tny_simple_list_new ();
949 /* Create the local folders account */
951 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
952 tny_list_append (priv->store_accounts, G_OBJECT(local_account));
953 g_object_unref (local_account);
955 /* Add the other remote accounts. Do this after adding the
956 local account, because we need to add our outboxes to the
957 global OUTBOX hosted in the local account */
958 add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
960 /* Add connection-specific transport accounts if there are any
961 accounts available */
962 if (!only_local_accounts (MODEST_TNY_ACCOUNT_STORE(obj)))
963 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
965 /* This is a singleton, so it does not need to be unrefed. */
966 if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
968 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */);
971 return MODEST_TNY_ACCOUNT_STORE(obj);
975 modest_tny_account_store_get_accounts (TnyAccountStore *self,
977 TnyGetAccountsRequestType request_type)
979 ModestTnyAccountStorePrivate *priv;
981 g_return_if_fail (self);
982 g_return_if_fail (TNY_IS_LIST(list));
984 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
986 switch (request_type) {
987 case TNY_ACCOUNT_STORE_BOTH:
988 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
989 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
991 case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
992 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
994 case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
995 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
998 g_return_if_reached ();
1001 /* Initialize session. Why do we need this ??? */
1002 tny_session_camel_set_initialized (priv->session);
1007 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
1009 ModestTnyAccountStorePrivate *priv;
1010 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1012 if (!priv->cache_dir)
1013 priv->cache_dir = g_build_filename (g_get_home_dir(),
1014 MODEST_DIR, MODEST_CACHE_DIR, NULL);
1015 return priv->cache_dir;
1020 * callers need to unref
1023 modest_tny_account_store_get_device (TnyAccountStore *self)
1025 ModestTnyAccountStorePrivate *priv;
1027 g_return_val_if_fail (self, NULL);
1029 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1032 return g_object_ref (G_OBJECT(priv->device));
1039 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1041 return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self),
1042 MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1049 modest_tny_account_store_alert (TnyAccountStore *self,
1050 TnyAccount *account,
1055 ModestTransportStoreProtocol proto =
1056 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
1057 const gchar* server_name = "";
1058 gchar *prompt = NULL;
1061 /* NOTE: account may be NULL in some cases */
1062 g_return_val_if_fail (error, FALSE);
1064 /* Get the server name: */
1066 server_name = tny_account_get_hostname (account);
1067 const gchar *proto_name = tny_account_get_proto (account);
1069 proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1071 g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__,
1072 tny_account_get_id (account));
1077 switch (error->code) {
1078 case TNY_SYSTEM_ERROR_CANCEL:
1079 /* Don't show waste the user's time by showing him a dialog telling
1080 * him that he has just cancelled something: */
1083 case TNY_SERVICE_ERROR_PROTOCOL:
1084 /* Like a BAD from IMAP (protocol error) */
1085 case TNY_SERVICE_ERROR_LOST_CONNECTION:
1086 /* Lost the connection with the service */
1087 case TNY_SERVICE_ERROR_UNAVAILABLE:
1088 /* You must be working online for this operation */
1089 case TNY_SERVICE_ERROR_CONNECT:
1091 case MODEST_PROTOCOL_STORE_POP:
1092 prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1095 case MODEST_PROTOCOL_STORE_IMAP:
1096 prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1099 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1100 prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1104 g_return_val_if_reached (FALSE);
1108 case TNY_SERVICE_ERROR_AUTHENTICATE:
1109 /* It seems that there's no better error to show with
1110 * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1111 * may appear if there's a timeout during auth */
1113 case MODEST_PROTOCOL_STORE_POP:
1114 prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1117 case MODEST_PROTOCOL_STORE_IMAP:
1118 prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1121 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1122 prompt = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
1126 g_return_val_if_reached (FALSE);
1130 case TNY_SERVICE_ERROR_CERTIFICATE:
1131 /* We'll show the proper dialog later */
1134 case TNY_SYSTEM_ERROR_MEMORY:
1135 /* Can't allocate memory for this operation */
1137 case TNY_SERVICE_ERROR_UNKNOWN:
1140 g_return_val_if_reached (FALSE);
1144 if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1145 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1148 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1150 /* Show the account dialog if it was wrong */
1151 if (error->code == TNY_SERVICE_ERROR_CONNECT ||
1152 error->code == TNY_SERVICE_ERROR_AUTHENTICATE)
1153 show_wrong_password_dialog (account);
1167 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1169 TnyAccountStoreIface *klass;
1171 g_return_if_fail (g);
1173 klass = (TnyAccountStoreIface *)g;
1175 klass->get_accounts =
1176 modest_tny_account_store_get_accounts;
1177 klass->get_cache_dir =
1178 modest_tny_account_store_get_cache_dir;
1180 modest_tny_account_store_get_device;
1182 modest_tny_account_store_alert;
1183 klass->find_account =
1184 modest_tny_account_store_find_account_by_url;
1188 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1189 ModestTnyGetPassFunc func)
1191 /* not implemented, we use signals */
1192 g_printerr ("modest: set_get_pass_func not implemented\n");
1196 modest_tny_account_store_get_session (TnyAccountStore *self)
1198 g_return_val_if_fail (self, NULL);
1199 return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1203 get_tny_account_by (TnyList *accounts,
1204 ModestTnyAccountStoreQueryType type,
1207 TnyIterator *iter = NULL;
1208 gboolean found = FALSE;
1209 TnyAccount *retval = NULL;
1211 g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1213 if (tny_list_get_length(accounts) == 0) {
1214 g_warning ("%s: account list is empty", __FUNCTION__);
1218 iter = tny_list_create_iterator (accounts);
1219 while (!tny_iterator_is_done (iter) && !found) {
1220 TnyAccount *tmp_account = NULL;
1221 const gchar *val = NULL;
1223 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1224 if (!TNY_IS_ACCOUNT(tmp_account)) {
1225 g_warning ("%s: not a valid account", __FUNCTION__);
1231 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1232 val = tny_account_get_id (tmp_account);
1234 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1235 val = tny_account_get_url_string (tmp_account);
1239 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL &&
1240 tny_account_matches_url_string (tmp_account, str)) {
1241 retval = g_object_ref (tmp_account);
1244 if (val && str && strcmp (val, str) == 0) {
1245 retval = g_object_ref (tmp_account);
1249 g_object_unref (tmp_account);
1250 tny_iterator_next (iter);
1252 g_object_unref (iter);
1258 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self,
1259 ModestTnyAccountStoreQueryType type,
1262 TnyAccount *account = NULL;
1263 ModestTnyAccountStorePrivate *priv;
1265 g_return_val_if_fail (self, NULL);
1266 g_return_val_if_fail (str, NULL);
1268 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1270 /* Search in store accounts */
1271 account = get_tny_account_by (priv->store_accounts, type, str);
1273 /* If we already found something, no need to search the transport accounts */
1275 account = get_tny_account_by (priv->transport_accounts, type, str);
1277 /* If we already found something, no need to search the
1278 per-account outbox accounts */
1280 account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1283 /* Warn if nothing was found. This is generally unusual. */
1285 g_warning("%s: Failed to find account with %s=%s\n",
1287 (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",
1291 /* Returns a new reference to the account if found */
1297 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1298 const gchar *account_name,
1299 TnyAccountType type)
1301 ModestTnyAccountStorePrivate *priv = NULL;
1302 TnyAccount *retval = NULL;
1303 TnyList *account_list = NULL;
1304 TnyIterator *iter = NULL;
1307 g_return_val_if_fail (self, NULL);
1308 g_return_val_if_fail (account_name, NULL);
1309 g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE ||
1310 type == TNY_ACCOUNT_TYPE_TRANSPORT,
1313 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1315 account_list = (type == TNY_ACCOUNT_TYPE_STORE) ?
1316 priv->store_accounts :
1317 priv->transport_accounts;
1319 if (!account_list) {
1320 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__,
1321 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1325 /* Look for the server account */
1327 iter = tny_list_create_iterator (account_list);
1328 while (!tny_iterator_is_done (iter) && !found) {
1329 const gchar *modest_acc_name;
1330 TnyAccount *tmp_account;
1332 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1334 modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1336 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1338 retval = g_object_ref (tmp_account);
1340 /* Free and continue */
1341 g_object_unref (tmp_account);
1342 tny_iterator_next (iter);
1344 g_object_unref (iter);
1347 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1348 "Number of server accounts of this type=%d\n", __FUNCTION__,
1349 (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1350 account_name, tny_list_get_length (account_list));
1353 /* Returns a new reference */
1358 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1359 ModestTnyAccountStore *self, const gchar *account_name)
1363 g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1364 g_return_val_if_fail (account_name, NULL);
1366 /* Get the current connection: */
1367 device = modest_runtime_get_device ();
1370 g_warning ("%s: could not get device", __FUNCTION__);
1374 if (!tny_device_is_online (device))
1377 #ifdef MODEST_HAVE_CONIC
1378 g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1380 TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);
1381 const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1382 /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1386 ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1390 const gchar *connection_id = con_ic_iap_get_id (connection);
1391 /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1395 /* Get the connection-specific transport acccount, if any: */
1396 ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1398 /* Check if this account has connection-specific SMTP enabled */
1399 if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1403 gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager,
1406 /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1407 if (!server_account_name) {
1408 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1411 TnyAccount* account = modest_tny_account_store_get_tny_account_by (self,
1412 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1413 server_account_name);
1415 /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1416 g_free (server_account_name);
1418 /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1419 g_object_unref (connection);
1423 return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1424 #endif /* MODEST_HAVE_CONIC */
1429 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1430 const gchar *account_name)
1432 g_return_val_if_fail (self, NULL);
1433 g_return_val_if_fail (account_name, NULL);
1435 if (!account_name || !self)
1438 /* Get the connection-specific transport acccount, if any: */
1439 /* Note: This gives us a reference: */
1440 TnyAccount *account =
1441 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1443 /* If there is no connection-specific transport account (the common case),
1444 * just get the regular transport account: */
1446 /* The special local folders don't have transport accounts. */
1447 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1450 /* Note: This gives us a reference: */
1451 account = modest_tny_account_store_get_server_account (self, account_name,
1452 TNY_ACCOUNT_TYPE_TRANSPORT);
1456 /* returns a reference. */
1461 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1463 TnyAccount *account = NULL;
1464 ModestTnyAccountStorePrivate *priv;
1468 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1470 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1473 iter = tny_list_create_iterator (priv->store_accounts);
1474 while (!tny_iterator_is_done (iter) && !found) {
1475 TnyAccount *tmp_account;
1477 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1478 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1479 account = g_object_ref (tmp_account);
1482 g_object_unref (tmp_account);
1483 tny_iterator_next (iter);
1485 g_object_unref (iter);
1487 /* Returns a new reference to the account */
1492 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1494 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1497 return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1498 MODEST_MMC_ACCOUNT_ID);
1502 /*********************************************************************************/
1504 add_existing_accounts (ModestTnyAccountStore *self)
1506 GSList *account_names = NULL, *iter = NULL;
1507 ModestTnyAccountStorePrivate *priv = NULL;
1509 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1511 /* These are account names, not server_account names */
1512 account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1514 for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1515 const gchar *account_name = (const gchar*) iter->data;
1517 /* Insert all enabled accounts without notifying */
1518 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1519 insert_account (self, account_name, FALSE);
1521 modest_account_mgr_free_account_names (account_names);
1525 connection_status_changed (TnyAccount *account,
1526 TnyConnectionStatus status,
1529 /* We do this here and not in the connection policy because we
1530 don't want to do it for every account, just for the
1531 accounts that are interactively added when modest is
1533 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1534 const gchar *account_name;
1535 ModestWindow *main_window;
1536 ModestTnyAccountStorePrivate *priv = NULL;
1538 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1540 /* Remove this handler */
1541 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1543 "connection_status_changed");
1545 /* Perform a send receive */
1546 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1547 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1548 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, FALSE, main_window);
1553 create_tny_account (ModestTnyAccountStore *self,
1555 TnyAccountType type,
1558 TnyAccount *account = NULL;
1559 ModestTnyAccountStorePrivate *priv = NULL;
1561 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1563 account = modest_tny_account_new_from_account (priv->account_mgr,
1570 /* Forget any cached password for the account, so that
1571 we use a new account if any */
1572 forget_password_in_memory (self, tny_account_get_id (account));
1574 /* Install a signal handler that will refresh the
1575 account the first time it becomes online. Do this
1576 only if we're adding a new account while the
1577 program is running (we do not want to do this
1579 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1580 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1582 "connection_status_changed",
1583 G_CALLBACK (connection_status_changed),
1586 /* Set the account store */
1587 g_object_set_data (G_OBJECT(account), "account_store", self);
1589 g_printerr ("modest: failed to create account for %s\n", name);
1595 typedef struct _AddOutboxInfo {
1596 ModestTnyAccountStore *account_store;
1597 TnyAccount *transport_account;
1601 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1607 TnyIterator *iter_folders;
1608 TnyFolder *per_account_outbox;
1609 TnyAccount *local_account = NULL;
1610 AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1611 ModestTnyAccountStorePrivate *priv = NULL;
1612 ModestTnyAccountStore *self;
1614 self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1615 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1617 if (tny_list_get_length (list) != 1) {
1618 g_warning ("%s: > 1 outbox found (%d)?!", __FUNCTION__,
1619 tny_list_get_length (list));
1622 iter_folders = tny_list_create_iterator (list);
1623 per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1624 g_object_unref (iter_folders);
1625 g_object_unref (list);
1627 /* Add the outbox of the new per-account-local-outbox account
1628 to the global local merged OUTBOX of the local folders
1630 local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1631 modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1632 per_account_outbox);
1633 /* Add the pair to the hash table */
1634 g_hash_table_insert (priv->outbox_of_transport,
1635 info->transport_account,
1636 per_account_outbox);
1638 /* Notify that the local account changed */
1639 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1641 g_object_unref (info->transport_account);
1642 g_object_unref (local_account);
1643 g_object_unref (per_account_outbox);
1644 g_slice_free (AddOutboxInfo, info);
1649 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1650 const gchar *account_name,
1651 TnyAccount *transport_account)
1653 TnyList *folders = NULL;
1654 TnyAccount *account_outbox = NULL;
1655 ModestTnyAccountStorePrivate *priv = NULL;
1656 AddOutboxInfo *info;
1658 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1660 /* Create per account local outbox */
1662 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr,
1666 if (!G_IS_OBJECT (account_outbox)) {
1667 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1671 tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1673 /* Get the outbox folder */
1674 folders = tny_simple_list_new ();
1675 info = g_slice_new0 (AddOutboxInfo);
1676 info->account_store = self;
1677 info->transport_account = g_object_ref (transport_account);
1678 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL,
1679 add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1680 g_object_unref (account_outbox);
1684 * This function will be used for both adding new accounts and for the
1685 * initialization. In the initialization we do not want to emit
1686 * signals so notify will be FALSE, in the case of account additions
1687 * we do want to notify the observers
1690 insert_account (ModestTnyAccountStore *self,
1691 const gchar *account,
1694 ModestTnyAccountStorePrivate *priv = NULL;
1695 TnyAccount *store_account = NULL, *transport_account = NULL;
1697 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1699 /* Get the server and the transport account */
1700 store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1701 if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1702 g_warning ("%s: failed to create store account", __FUNCTION__);
1706 transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1707 if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1708 g_warning ("%s: failed to create transport account", __FUNCTION__);
1709 g_object_unref (store_account);
1713 /* Add accounts to the lists */
1714 tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1715 tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1717 /* Create a new pseudo-account with an outbox for this
1718 transport account and add it to the global outbox
1719 in the local account */
1720 add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1722 /* Notify the observers. We do it after everything is
1725 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1726 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1730 g_object_unref (store_account);
1731 g_object_unref (transport_account);
1735 only_local_accounts (ModestTnyAccountStore *self)
1737 ModestTnyAccountStorePrivate *priv = NULL;
1738 gboolean only_local = TRUE;
1741 /* Check if this is the first remote account we add */
1742 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1743 iter = tny_list_create_iterator (priv->store_accounts);
1745 while (!tny_iterator_is_done (iter) && only_local) {
1746 TnyAccount *account = (TnyAccount *) tny_iterator_get_current (iter);
1747 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1749 g_object_unref (account);
1750 tny_iterator_next (iter);
1752 g_object_unref (iter);
1758 on_account_inserted (ModestAccountMgr *acc_mgr,
1759 const gchar *account,
1762 gboolean add_specific;
1764 add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1766 /* Insert the account and notify the observers */
1767 insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1769 /* If it's the first remote account then add the connection
1770 specific SMTP servers as well */
1772 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1776 /* This is the callback of the tny_camel_account_set_online called in
1777 on_account_removed to disconnect the account */
1779 on_account_disconnect_when_removing (TnyCamelAccount *account,
1784 ModestTnyAccountStore *self;
1785 ModestTnyAccountStorePrivate *priv;
1787 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1788 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1790 /* Remove the connection-status-changed handler if it's still there */
1791 if (modest_signal_mgr_is_connected (priv->sighandlers,
1793 "connection_status_changed")) {
1794 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1796 "connection_status_changed");
1799 /* Cancel all pending operations */
1800 tny_account_cancel (TNY_ACCOUNT (account));
1802 /* Unref the extra reference added by get_server_account */
1803 g_object_unref (account);
1805 /* Clear the cache if it's an store account */
1806 if (TNY_IS_STORE_ACCOUNT (account)) {
1807 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1808 } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1809 ModestTnySendQueue* send_queue;
1810 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1812 if (modest_tny_send_queue_sending_in_progress (send_queue))
1813 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1814 TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE,
1816 modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1822 * We use this one for both removing "normal" and "connection
1823 * specific" transport accounts
1826 remove_transport_account (ModestTnyAccountStore *self,
1827 TnyTransportAccount *transport_account)
1829 ModestTnyAccountStorePrivate *priv;
1830 TnyFolder *outbox = NULL;
1832 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1834 /* Remove it from the list of accounts and notify the
1835 observers. Do not need to wait for account
1837 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1838 tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1840 /* Remove the OUTBOX of the account from the global outbox */
1841 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1843 if (TNY_IS_FOLDER (outbox)) {
1844 TnyAccount *local_account = NULL;
1845 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1847 if (outbox_account) {
1848 tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1849 /* Remove existing emails to send */
1850 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1851 g_object_unref (outbox_account);
1854 local_account = modest_tny_account_store_get_local_folders_account (self);
1855 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1858 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1860 /* Notify the change in the local account */
1861 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1862 g_object_unref (local_account);
1864 g_warning ("Removing a transport account that has no outbox");
1867 /* Cancel all pending operations */
1868 tny_account_cancel (TNY_ACCOUNT (transport_account));
1870 /* Disconnect and notify the observers. The callback will free the reference */
1871 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1872 on_account_disconnect_when_removing, self);
1876 on_account_removed (ModestAccountMgr *acc_mgr,
1877 const gchar *account,
1880 TnyAccount *store_account = NULL, *transport_account = NULL;
1881 ModestTnyAccountStore *self;
1882 ModestTnyAccountStorePrivate *priv;
1884 self = MODEST_TNY_ACCOUNT_STORE (user_data);
1885 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1887 /* Get the server and the transport account */
1889 modest_tny_account_store_get_server_account (self, account,
1890 TNY_ACCOUNT_TYPE_STORE);
1892 modest_tny_account_store_get_server_account (self, account,
1893 TNY_ACCOUNT_TYPE_TRANSPORT);
1895 /* If there was any problem creating the account, for example,
1896 with the configuration system this could not exist */
1897 if (TNY_IS_STORE_ACCOUNT(store_account)) {
1898 /* Forget any cached password for the account */
1899 forget_password_in_memory (self, tny_account_get_id (store_account));
1901 /* Remove it from the list of accounts and notify the
1902 observers. Do not need to wait for account
1904 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1905 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1907 /* Cancel all pending operations */
1908 tny_account_cancel (TNY_ACCOUNT (store_account));
1910 /* Disconnect before deleting the cache, because the
1911 disconnection will rewrite the cache to the
1913 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1914 on_account_disconnect_when_removing, self);
1916 g_warning ("%s: no store account for account %s\n",
1917 __FUNCTION__, account);
1920 /* If there was any problem creating the account, for example,
1921 with the configuration system this could not exist */
1922 if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1924 /* Forget any cached password for the account */
1925 forget_password_in_memory (self, tny_account_get_id (transport_account));
1927 /* Remove transport account. It'll free the reference
1928 added by get_server_account */
1929 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1931 g_warning ("%s: no transport account for account %s\n",
1932 __FUNCTION__, account);
1935 /* If there are no more user accounts then delete the
1936 transport specific SMTP servers */
1937 if (only_local_accounts (self))
1938 remove_connection_specific_transport_accounts (self);
1941 TnyTransportAccount *
1942 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1945 ModestTnyAccountStorePrivate *priv = NULL;
1946 TnyAccount * tny_account = NULL;
1948 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1950 /* Add the account: */
1952 modest_tny_account_new_from_server_account_name (priv->account_mgr,
1958 g_object_set_data (G_OBJECT(tny_account),
1961 g_object_set_data (G_OBJECT(tny_account),
1962 "connection_specific",
1963 GINT_TO_POINTER (TRUE));
1965 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1966 add_outbox_from_transport_account_to_global_outbox (self,
1971 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1974 return TNY_TRANSPORT_ACCOUNT (tny_account);
1979 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1981 ModestTnyAccountStorePrivate *priv = NULL;
1982 GSList *list_specifics = NULL, *iter = NULL;
1985 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1987 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
1988 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1989 MODEST_CONF_VALUE_STRING, &err);
1992 g_return_if_reached ();
1995 /* Look at each connection-specific transport account for the
1996 * modest account: */
1997 iter = list_specifics;
1999 /* The list alternates between the connection name and the transport name: */
2000 iter = g_slist_next (iter);
2002 const gchar* transport_account_name = (const gchar*) (iter->data);
2003 TnyTransportAccount * account = NULL;
2004 account = modest_tny_account_store_new_connection_specific_transport_account (
2005 self, transport_account_name);
2007 g_object_unref (account);
2009 iter = g_slist_next (iter);
2014 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2016 ModestTnyAccountStorePrivate *priv = NULL;
2017 GSList *list_specifics = NULL, *iter = NULL;
2020 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2023 list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2024 MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2025 MODEST_CONF_VALUE_STRING, &err);
2028 g_return_if_reached ();
2031 /* Look at each connection-specific transport account for the
2032 * modest account: */
2033 iter = list_specifics;
2035 /* The list alternates between the connection name and the transport name: */
2036 iter = g_slist_next (iter);
2038 const gchar* transport_account_name = (const gchar*) (iter->data);
2039 TnyAccount * account;
2040 account = modest_tny_account_store_get_server_account (self,
2041 transport_account_name,
2042 TNY_ACCOUNT_TYPE_TRANSPORT);
2044 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2045 g_object_unref (account);
2048 iter = g_slist_next (iter);
2054 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self,
2056 TnyAccount **ac_out)
2058 TnyIterator *acc_iter;
2059 ModestTnyAccountStorePrivate *priv;
2061 TnyAccount *msg_account = NULL;
2063 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2064 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2066 acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2067 while (!msg && !tny_iterator_is_done (acc_iter)) {
2068 TnyList *folders = tny_simple_list_new ();
2069 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2070 TnyIterator *folders_iter = NULL;
2072 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
2073 folders_iter = tny_list_create_iterator (folders);
2075 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2076 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2077 msg = tny_folder_find_msg (folder, uri, NULL);
2080 msg_account = g_object_ref (account);
2082 g_object_unref (folder);
2083 tny_iterator_next (folders_iter);
2085 g_object_unref (folders_iter);
2087 g_object_unref (folders);
2088 g_object_unref (account);
2089 tny_iterator_next (acc_iter);
2092 g_object_unref (acc_iter);
2095 *ac_out = msg_account;
2100 TnyTransportAccount *
2101 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2103 TnyIterator *acc_iter;
2104 ModestTnyAccountStorePrivate *priv;
2105 TnyTransportAccount *header_acc = NULL;
2108 g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2109 g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2110 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2112 msg_id = modest_tny_send_queue_get_msg_id (header);
2113 acc_iter = tny_list_create_iterator (priv->transport_accounts);
2114 while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2115 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2116 ModestTnySendQueue *send_queue;
2117 ModestTnySendQueueStatus status;
2118 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2119 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2120 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
2121 header_acc = g_object_ref(account);
2123 g_object_unref (account);
2124 tny_iterator_next (acc_iter);
2126 g_object_unref(acc_iter);
2134 modest_tny_account_store_show_account_settings_dialog (ModestTnyAccountStore *self,
2135 const gchar *account_name)
2137 ModestTnyAccountStorePrivate *priv;
2138 gpointer dialog_as_gpointer = NULL;
2141 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2142 found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
2143 account_name, NULL, (gpointer*)&dialog_as_gpointer);
2146 modest_account_settings_dialog_check_allow_changes ((ModestAccountSettingsDialog *) dialog_as_gpointer);
2147 return (GtkWidget *) dialog_as_gpointer;
2149 ModestAccountSettings *settings;
2151 dialog = (GtkWidget *) modest_account_settings_dialog_new ();
2152 settings = modest_account_mgr_load_account_settings (priv->account_mgr, account_name);
2153 modest_account_settings_dialog_set_account (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog), settings);
2154 g_object_unref (settings);
2155 modest_account_settings_dialog_switch_to_user_info (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2156 modest_account_settings_dialog_check_allow_changes (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2157 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
2159 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (account_name), dialog);
2161 g_signal_connect (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide),
2162 g_strdup (account_name));
2164 /* Show it and delete it when it closes: */
2165 g_signal_connect_swapped (dialog,
2167 G_CALLBACK (gtk_widget_destroy),
2169 gtk_widget_show (GTK_WIDGET (dialog));
2177 ModestTnyAccountStore *account_store;
2178 ModestTnyAccountStoreShutdownCallback callback;
2184 account_shutdown_callback (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer userdata)
2186 ShutdownOpData *op_data = (ShutdownOpData *) userdata;
2188 if (op_data->pending == 0) {
2189 if (op_data->callback)
2190 op_data->callback (op_data->account_store, op_data->userdata);
2191 g_object_unref (op_data->account_store);
2197 account_shutdown (TnyAccount *account, ShutdownOpData *op_data)
2199 g_return_if_fail (account && TNY_IS_ACCOUNT(account));
2201 if (TNY_IS_STORE_ACCOUNT (account) &&
2202 !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
2207 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, account_shutdown_callback, op_data);
2212 modest_tny_account_store_shutdown (ModestTnyAccountStore *self,
2213 ModestTnyAccountStoreShutdownCallback callback,
2216 ShutdownOpData *op_data = g_new0 (ShutdownOpData, 1);
2217 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2218 op_data->callback = callback;
2219 op_data->userdata = userdata;
2220 op_data->pending = 0;
2221 op_data->account_store = g_object_ref (self);
2223 /* Destroy all accounts. Disconnect all accounts before they are destroyed */
2224 if (priv->store_accounts) {
2225 tny_list_foreach (priv->store_accounts, (GFunc)account_shutdown, op_data);
2228 if (priv->transport_accounts) {
2229 tny_list_foreach (priv->transport_accounts, (GFunc)account_shutdown, op_data);
2232 if (op_data->pending == 0) {
2233 if (op_data->callback)
2234 op_data->callback (op_data->account_store, op_data->userdata);
2235 g_object_unref (op_data->account_store);