* Fixes NB#86984, do not show authentication errors when the device disconnects
[modest] / src / modest-tny-account-store.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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.
16  *
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.
28  */
29
30 #include <string.h>
31 #include <glib/gi18n.h>
32
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>
43
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>
55
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>
63
64 #ifdef MODEST_PLATFORM_MAEMO
65 #include <tny-maemo-conic-device.h>
66 #include <maemo/modest-maemo-utils.h>
67 #endif
68
69 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
70
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);
77
78 static void    on_account_inserted         (ModestAccountMgr *acc_mgr, 
79                                             const gchar *account,
80                                             gpointer user_data);
81
82 static void   add_existing_accounts       (ModestTnyAccountStore *self);
83
84 static void    insert_account              (ModestTnyAccountStore *self,
85                                             const gchar *account,
86                                             gboolean notify);
87
88 static void    on_account_removed          (ModestAccountMgr *acc_mgr, 
89                                             const gchar *account,
90                                             gpointer user_data);
91
92 static gchar*  get_password                (TnyAccount *account, 
93                                             const gchar * prompt_not_used, 
94                                             gboolean *cancel);
95
96 static void    forget_password             (TnyAccount *account);
97
98 static void    on_vfs_volume_mounted       (GnomeVFSVolumeMonitor *volume_monitor, 
99                                             GnomeVFSVolume *volume, 
100                                             gpointer user_data);
101
102 static void    on_vfs_volume_unmounted     (GnomeVFSVolumeMonitor *volume_monitor, 
103                                             GnomeVFSVolume *volume, 
104                                             gpointer user_data);
105
106 static void    forget_password_in_memory (ModestTnyAccountStore *self, 
107                                           const gchar *server_account_name);
108
109 static void    add_connection_specific_transport_accounts         (ModestTnyAccountStore *self);
110
111 static void    remove_connection_specific_transport_accounts      (ModestTnyAccountStore *self);
112
113 static void    connection_status_changed   (TnyAccount *account, 
114                                             TnyConnectionStatus status, 
115                                             gpointer data);
116
117 static gboolean only_local_accounts        (ModestTnyAccountStore *self);
118
119 /* list my signals */
120 enum {
121         ACCOUNT_CHANGED_SIGNAL,
122         ACCOUNT_INSERTED_SIGNAL,
123         ACCOUNT_REMOVED_SIGNAL,
124
125         PASSWORD_REQUESTED_SIGNAL,
126         LAST_SIGNAL
127 };
128
129 typedef struct _ModestTnyAccountStorePrivate ModestTnyAccountStorePrivate;
130 struct _ModestTnyAccountStorePrivate {
131         gchar              *cache_dir;  
132         GHashTable         *password_hash;
133         GHashTable         *account_settings_dialog_hash;
134         ModestAccountMgr   *account_mgr;
135         TnySessionCamel    *session;
136         TnyDevice          *device;
137
138         GSList *sighandlers;
139         
140         /* We cache the lists of accounts here */
141         TnyList             *store_accounts;
142         TnyList             *transport_accounts;
143         TnyList             *store_accounts_outboxes;
144         
145         /* Matches transport accounts and outbox folder */
146         GHashTable          *outbox_of_transport;
147 };
148
149 #define MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
150                                                       MODEST_TYPE_TNY_ACCOUNT_STORE, \
151                                                       ModestTnyAccountStorePrivate))
152
153 /* globals */
154 static GObjectClass *parent_class = NULL;
155
156 static guint signals[LAST_SIGNAL] = {0};
157
158 GType
159 modest_tny_account_store_get_type (void)
160 {
161         static GType my_type = 0;
162
163         if (!my_type) {
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),
172                         0,              /* n_preallocs */
173                         (GInstanceInitFunc) modest_tny_account_store_instance_init,
174                         NULL
175                 };
176
177                 static const GInterfaceInfo iface_info = {
178                         (GInterfaceInitFunc) modest_tny_account_store_init,
179                         NULL,         /* interface_finalize */
180                         NULL          /* interface_data */
181                 };
182                 /* hack hack */
183                 my_type = g_type_register_static (G_TYPE_OBJECT,
184                                                   "ModestTnyAccountStore",
185                                                   &my_info, 0);
186                 g_type_add_interface_static (my_type, TNY_TYPE_ACCOUNT_STORE,
187                                              &iface_info);
188         }
189         return my_type;
190 }
191
192
193 static void
194 modest_tny_account_store_base_init (gpointer g_class)
195 {
196         static gboolean tny_account_store_initialized = FALSE;
197
198         if (!tny_account_store_initialized) {
199
200                 signals[ACCOUNT_CHANGED_SIGNAL] =
201                         g_signal_new ("account_changed",
202                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
203                                       G_SIGNAL_RUN_FIRST,
204                                       G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_changed),
205                                       NULL, NULL,
206                                       g_cclosure_marshal_VOID__OBJECT,
207                                       G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
208
209                 signals[ACCOUNT_INSERTED_SIGNAL] =
210                         g_signal_new ("account_inserted",
211                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
212                                       G_SIGNAL_RUN_FIRST,
213                                       G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_inserted),
214                                       NULL, NULL,
215                                       g_cclosure_marshal_VOID__OBJECT,
216                                       G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
217                 
218                 signals[ACCOUNT_REMOVED_SIGNAL] =
219                         g_signal_new ("account_removed",
220                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
221                                       G_SIGNAL_RUN_FIRST,
222                                       G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_removed),
223                                       NULL, NULL,
224                                       g_cclosure_marshal_VOID__OBJECT,
225                                       G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
226
227                 signals[PASSWORD_REQUESTED_SIGNAL] =
228                         g_signal_new ("password_requested",
229                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
230                                       G_SIGNAL_RUN_FIRST,
231                                       G_STRUCT_OFFSET(ModestTnyAccountStoreClass, password_requested),
232                                       NULL, NULL,
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,
235                                       G_TYPE_POINTER);          
236
237                 tny_account_store_initialized = TRUE;
238         }
239 }
240
241
242 static void
243 modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass)
244 {
245         GObjectClass *gobject_class;
246         gobject_class = (GObjectClass*) klass;
247
248         parent_class            = g_type_class_peek_parent (klass);
249         gobject_class->finalize = modest_tny_account_store_finalize;
250
251         g_type_class_add_private (gobject_class,
252                                   sizeof(ModestTnyAccountStorePrivate));
253 }
254      
255 static void
256 modest_tny_account_store_instance_init (ModestTnyAccountStore *obj)
257 {
258         GnomeVFSVolumeMonitor* monitor = NULL;
259         ModestTnyAccountStorePrivate *priv;
260
261         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
262
263         priv->cache_dir              = NULL;
264         priv->account_mgr            = NULL;
265         priv->session                = NULL;
266         priv->device                 = NULL;
267         priv->sighandlers            = NULL;
268         
269         priv->outbox_of_transport = g_hash_table_new_full (g_direct_hash,
270                                                            g_direct_equal,
271                                                            NULL,
272                                                            NULL);
273
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:
277          */
278         priv->password_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
279                                                      g_free, g_free);
280
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, 
284                                                                     g_free, NULL);
285         
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();
290
291         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, 
292                                                        G_OBJECT(monitor), 
293                                                        "volume-mounted",
294                                                        G_CALLBACK(on_vfs_volume_mounted),
295                                                        obj);
296         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, 
297                                                        G_OBJECT(monitor), "volume-unmounted",
298                                                        G_CALLBACK(on_vfs_volume_unmounted),
299                                                        obj);
300 }
301
302 /* disconnect the list of TnyAccounts */
303 static void
304 account_disconnect (TnyAccount *account)
305 {
306         g_return_if_fail (account && TNY_IS_ACCOUNT(account));
307
308         if (TNY_IS_STORE_ACCOUNT (account) && 
309             !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
310                 return;
311
312         tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, NULL, NULL);
313 }
314
315
316 /* disconnect the list of TnyAccounts */
317 static void
318 account_verify_last_ref (TnyAccount *account, const gchar *str)
319 {
320         gchar *txt;
321
322         g_return_if_fail (account && TNY_IS_ACCOUNT(account));
323
324         txt = g_strdup_printf ("%s: %s", str ? str : "?", tny_account_get_name(account));
325         MODEST_DEBUG_VERIFY_OBJECT_LAST_REF(G_OBJECT(account),txt);
326         g_free (txt);
327 }
328
329
330
331
332 static void
333 foreach_account_append_to_list (gpointer data, 
334                                 gpointer user_data)
335 {
336         TnyList *list;
337         
338         list = TNY_LIST (user_data);
339         tny_list_append (list, G_OBJECT (data));
340 }
341
342 /********************************************************************/
343 /*           Control the state of the MMC local account             */
344 /********************************************************************/
345
346 /** Only call this if the memory card is really mounted.
347  */ 
348 static void
349 add_mmc_account(ModestTnyAccountStore *self, gboolean emit_insert_signal)
350 {
351         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
352         g_return_if_fail (priv->session);
353         
354         TnyAccount *mmc_account = modest_tny_account_new_for_local_folders (priv->account_mgr, 
355                                                                         priv->session, 
356                                                                         MODEST_MCC1_VOLUMEPATH);
357
358         /* Add to the list of store accounts */
359         tny_list_append (priv->store_accounts, G_OBJECT (mmc_account));
360
361         if (emit_insert_signal) {
362                 g_signal_emit (G_OBJECT (self), 
363                                signals [ACCOUNT_INSERTED_SIGNAL],
364                                0, mmc_account);
365         }
366
367         /* Free */
368         g_object_unref (mmc_account);
369 }
370
371 static void
372 on_vfs_volume_mounted(GnomeVFSVolumeMonitor *volume_monitor, 
373                       GnomeVFSVolume *volume, 
374                       gpointer user_data)
375 {
376         ModestTnyAccountStore *self;
377         ModestTnyAccountStorePrivate *priv;
378  
379         gchar *uri = NULL;
380
381         self = MODEST_TNY_ACCOUNT_STORE(user_data);
382         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
383         
384         /* Check whether this was the external MMC1 card: */
385         uri = gnome_vfs_volume_get_activation_uri (volume);
386
387         if (uri && (!strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI))) {
388                 add_mmc_account (self, TRUE /* emit the insert signal. */);
389         }
390         
391         g_free (uri);
392 }
393
394 static void
395 on_vfs_volume_unmounted(GnomeVFSVolumeMonitor *volume_monitor, 
396                         GnomeVFSVolume *volume, 
397                         gpointer user_data)
398 {
399         ModestTnyAccountStore *self;
400         ModestTnyAccountStorePrivate *priv;
401         gchar *uri = NULL;
402
403         self = MODEST_TNY_ACCOUNT_STORE(user_data);
404         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
405         
406         /* Check whether this was the external MMC1 card: */
407         uri = gnome_vfs_volume_get_activation_uri (volume);
408         if (uri && (strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI) == 0)) {
409                 TnyAccount *mmc_account = NULL;
410                 gboolean found = FALSE;
411                 TnyIterator *iter = NULL;
412
413                 iter = tny_list_create_iterator (priv->store_accounts);
414                 while (!tny_iterator_is_done (iter) && !found) {
415                         TnyAccount *account;
416
417                         account = TNY_ACCOUNT (tny_iterator_get_current (iter));
418                         if (modest_tny_account_is_memory_card_account (account)) {
419                                 found = TRUE;
420                                 mmc_account = g_object_ref (account);
421                         }
422                         g_object_unref (account);
423                         tny_iterator_next (iter);
424                 }
425                 g_object_unref (iter);
426
427                 if (found) {
428                         /* Remove from the list */
429                         tny_list_remove (priv->store_accounts, G_OBJECT (mmc_account));
430                         
431                         /* Notify observers */
432                         g_signal_emit (G_OBJECT (self),
433                                        signals [ACCOUNT_REMOVED_SIGNAL],
434                                        0, mmc_account);
435
436                         g_object_unref (mmc_account);
437                 } else {
438                         g_warning ("%s: there was no store account for the unmounted MMC",
439                                    __FUNCTION__);
440                 }
441         }
442         g_free (uri);
443 }
444
445 /**
446  * forget_password_in_memory
447  * @self: a TnyAccountStore instance
448  * @account: A server account.
449  * 
450  * Forget any password stored in memory for this account.
451  * For instance, this should be called when the user has changed the password in the account settings.
452  */
453 static void
454 forget_password_in_memory (ModestTnyAccountStore *self, 
455                            const gchar * server_account_name)
456 {
457         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
458
459         if (server_account_name && priv->password_hash) {
460                 g_hash_table_remove (priv->password_hash, server_account_name);
461         }
462 }
463
464 static void
465 on_account_changed (ModestAccountMgr *acc_mgr, 
466                     const gchar *account_name, 
467                     TnyAccountType account_type,
468                     gpointer user_data)
469 {
470         ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
471         ModestTnyAccountStorePrivate *priv;
472         TnyList* account_list;
473         gboolean found = FALSE;
474         TnyIterator *iter = NULL;
475
476         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
477         account_list = (account_type == TNY_ACCOUNT_TYPE_STORE ? 
478                         priv->store_accounts : 
479                         priv->transport_accounts);
480         
481         iter = tny_list_create_iterator (account_list);
482         while (!tny_iterator_is_done (iter) && !found) {
483                 TnyAccount *tny_account;
484                 tny_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
485                 if (tny_account) {
486                         if (!strcmp (tny_account_get_id (tny_account), account_name)) {
487                                 found = TRUE;
488                                 modest_tny_account_update_from_account (tny_account, get_password, forget_password);
489                                 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_CHANGED_SIGNAL], 0, tny_account);
490                         }
491                         g_object_unref (tny_account);
492                 }
493                 tny_iterator_next (iter);
494         }
495
496         if (iter)
497                 g_object_unref (iter);
498 }
499
500 static void 
501 on_account_settings_hide (GtkWidget *widget, gpointer user_data)
502 {
503         /* This is easier than using a struct for the user_data: */
504         ModestTnyAccountStore *self = modest_runtime_get_account_store();
505         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
506         
507         gchar *account_name = (gchar *) user_data;
508         if (account_name)
509                 g_hash_table_remove (priv->account_settings_dialog_hash, account_name);
510 }
511
512 static void 
513 show_password_warning_only (const gchar *msg)
514 {
515         ModestWindow *main_window = 
516                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE); /* don't create */
517         
518         /* Show an explanatory temporary banner: */
519         if (main_window) 
520                 modest_platform_information_banner (NULL, NULL, msg);
521 }
522
523 static void 
524 show_wrong_password_dialog (TnyAccount *account)
525
526         /* This is easier than using a struct for the user_data: */
527         ModestTnyAccountStore *self = modest_runtime_get_account_store();
528         GtkWidget *main_window;
529         GtkWidget *dialog = NULL;
530
531         main_window = (GtkWidget *) modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
532                                                                        FALSE); /* don't create */
533         if (!main_window) {
534                 g_warning ("%s: password was wrong; ignoring because no main window", __FUNCTION__);
535                 return;
536         }
537
538         if (g_object_get_data (G_OBJECT (account), "connection_specific") != NULL) {
539                 modest_ui_actions_on_smtp_servers (NULL, NULL);
540         } else {
541                 const gchar *modest_account_name;
542                 modest_account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);   
543                 dialog = modest_tny_account_store_show_account_settings_dialog (self, modest_account_name);
544         }
545         /* Show an explanatory temporary banner: */
546         modest_platform_information_banner (dialog, NULL, _("mcen_ib_username_pw_incorrect"));
547 }
548
549 /* This callback will be called by Tinymail when it needs the password
550  * from the user or the account settings.
551  * It can also call forget_password() before calling this,
552  * so that we clear wrong passwords out of our account settings.
553  * Note that TnyAccount here will be the server account. */
554 static gchar*
555 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
556 {
557         ModestTnyAccountStore *self = NULL;
558         ModestTnyAccountStorePrivate *priv;
559         gchar *username = NULL;
560         gchar *pwd = NULL;
561         gpointer pwd_ptr = NULL;
562         gboolean already_asked = FALSE;
563         const gchar *server_account_name;
564         gchar *url_string;
565
566         g_return_val_if_fail (account, NULL);
567         
568         MODEST_DEBUG_BLOCK(
569                 g_debug ("%s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
570         );              
571
572         /* Get a reference to myself */
573         self = MODEST_TNY_ACCOUNT_STORE (g_object_get_data (G_OBJECT(account), "account_store"));
574         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
575         
576         /* Ensure that we still have this account. It could happen
577            that a set_online was requested *before* removing an
578            account, and due to tinymail emits the get_password
579            function using a g_idle the account could be actually
580            removed *before* this function was really called */
581         url_string = tny_account_get_url_string (account);
582         if (url_string) {
583                 TnyAccount *tmp_account;
584
585                 tmp_account = tny_account_store_find_account (TNY_ACCOUNT_STORE (self), 
586                                                               url_string);
587                 g_free (url_string);
588
589                 if (!tmp_account) {
590                         *cancel = TRUE;
591                         return NULL;
592                 }
593                 g_object_unref (tmp_account);
594         }
595
596         server_account_name = tny_account_get_id (account);
597         if (!server_account_name || !self) {
598                 g_warning ("modest: %s: could not retrieve account_store for account %s",
599                            __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
600                 if (cancel)
601                         *cancel = TRUE;
602                 
603                 return NULL;
604         }
605         
606         /* This hash map stores passwords, including passwords that are not stored in gconf. */
607         /* Is it in the hash? if it's already there, it must be wrong... */
608         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
609                                    * type-punned ptrs...*/
610         already_asked = priv->password_hash && 
611                                 g_hash_table_lookup_extended (priv->password_hash,
612                                                       server_account_name,
613                                                       NULL,
614                                                       (gpointer*)&pwd_ptr);
615         MODEST_DEBUG_BLOCK(
616                 g_debug ("%s: Already asked = %d\n", __FUNCTION__, already_asked);
617         );
618                 
619         /* If the password is not already there, try ModestConf */
620         if (!already_asked) {
621                 pwd  = modest_account_mgr_get_server_account_password (priv->account_mgr,
622                                                                        server_account_name);
623                 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
624         }
625
626         /* If it was already asked, it must have been wrong, so ask again */
627         if (already_asked || !pwd || strlen(pwd) == 0) {
628                 /* As per the UI spec, if no password was set in the account settings, 
629                  * ask for it now. But if the password is wrong in the account settings, 
630                  * then show a banner and the account settings dialog so it can be corrected:
631                  */
632                 ModestTransportStoreProtocol proto;
633                 const gboolean settings_have_password = 
634                         modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
635
636                 /* Show an error and after that ask for a password */
637                 proto = modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (account));
638                 if (proto == MODEST_PROTOCOL_TRANSPORT_SMTP) {
639                         gchar *username = NULL, *msg = NULL;
640                         username = modest_account_mgr_get_server_account_username (priv->account_mgr,
641                                                                                    server_account_name);
642                         if (!username || strlen(username) == 0) {
643                                 msg = g_strdup_printf (_("emev_ni_ui_smtp_userid_invalid"), 
644                                                        tny_account_get_name (account),
645                                                        tny_account_get_hostname (account));
646                         } else {
647                                 gchar *password;
648                                 password  = modest_account_mgr_get_server_account_password (priv->account_mgr,
649                                                                                             server_account_name);
650                                 if (!password || strlen(password) == 0)
651                                         msg = g_strdup_printf (_("emev_ni_ui_smtp_passwd_invalid"), 
652                                                                tny_account_get_name (account),
653                                                                tny_account_get_hostname (account));
654                                 else
655                                         msg = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"), 
656                                                                tny_account_get_hostname (account));
657                                 if (password)
658                                         g_free (password);
659                         }
660                         if (msg) {
661                                 modest_platform_run_information_dialog (NULL, msg, TRUE);
662                                 g_free (msg);
663                         }
664                         if (username)
665                                 g_free (username);
666                 }
667
668                 if (settings_have_password) {
669                         /* The password must be wrong, so show the account settings dialog so it can be corrected: */
670                         show_wrong_password_dialog (account);
671                         
672                         if (cancel)
673                                 *cancel = TRUE;
674                                 
675                         return NULL;
676                 }
677         
678                 /* we don't have it yet. Get the password from the user */
679                 const gchar* account_id = tny_account_get_id (account);
680                 gboolean remember = FALSE;
681                 pwd = NULL;
682                 
683                 if (already_asked) {
684                         const gchar *msg;
685                         gboolean username_known = 
686                                 modest_account_mgr_get_server_account_username_has_succeeded(priv->account_mgr, 
687                                                                                              server_account_name);
688                         /* If the login has ever succeeded then show a specific message */
689                         if (username_known)
690                                 msg = dgettext ("hildon-common-strings", "ecdg_ib_set_password_incorrect");
691                         else
692                                 msg = _("mcen_ib_username_pw_incorrect");
693                         show_password_warning_only (msg);
694                 }
695
696                 /* Request password */
697                 g_signal_emit (G_OBJECT (self), signals[PASSWORD_REQUESTED_SIGNAL], 0,
698                                account_id, /* server_account_name */
699                                &username, &pwd, cancel, &remember);
700
701                 
702                 if (!*cancel) {
703                         /* The password will be returned as the result,
704                          * but we need to tell tinymail about the username too: */
705
706                         /* WARNING: I disabled setting username as this can cause locks. Anyway,
707                          * as now we have the password dialog username entry always dimmed
708                          * this shouldn't be a problem */
709
710                         /* if (username) */
711                         /*      tny_account_set_user (account, username); */
712                         
713                         /* Do not save the password in gconf, because
714                          * the UI spec says "The password will never
715                          * be saved in the account": */
716
717                         /* We need to dup the string even knowing that
718                            it's already a dup of the contents of an
719                            entry, because it if it's wrong, then camel
720                            will free it */
721                         g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
722                 } else {
723                         g_hash_table_remove (priv->password_hash, server_account_name);
724                         
725                         g_free (pwd);
726                         pwd = NULL;
727                 }
728
729                 g_free (username);
730                 username = NULL;
731         } else
732                 if (cancel)
733                         *cancel = FALSE;        
734         return pwd;
735 }
736
737 void 
738 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
739 {
740         g_return_if_fail (account);
741           
742         ModestTnyAccountStorePrivate *priv;
743         gchar *pwd = NULL;
744         gpointer pwd_ptr = NULL;
745         gboolean already_asked = FALSE;
746                 
747         const gchar *server_account_name = tny_account_get_id (account);
748
749         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
750         
751         /* This hash map stores passwords, including passwords that are not stored in gconf. */
752         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
753                                    * type-punned ptrs...*/
754         already_asked = priv->password_hash && 
755                                 g_hash_table_lookup_extended (priv->password_hash,
756                                                       server_account_name,
757                                                       NULL,
758                                                       (gpointer*)&pwd_ptr);
759
760         if (already_asked) {
761                 g_hash_table_remove (priv->password_hash, server_account_name);         
762                 g_free (pwd);
763                 pwd = NULL;
764         }
765
766         return;
767 }
768
769 /* tinymail calls this if the connection failed due to an incorrect password.
770  * And it seems to call this for any general connection failure. */
771 static void
772 forget_password (TnyAccount *account)
773 {
774         ModestTnyAccountStore *self;
775         ModestTnyAccountStorePrivate *priv;
776         const TnyAccountStore *account_store;
777         gchar *pwd;
778         const gchar *key;
779         
780         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
781                                                              "account_store"));
782         self = MODEST_TNY_ACCOUNT_STORE (account_store);
783         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
784         key  = tny_account_get_id (account);
785
786         /* Do not remove the key, this will allow us to detect that we
787            have already asked for it at least once */
788         pwd = g_hash_table_lookup (priv->password_hash, key);
789         if (pwd) {
790                 memset (pwd, 0, strlen (pwd));
791                 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
792         }
793
794         /* Remove from configuration system */
795         /*
796         modest_account_mgr_unset (priv->account_mgr,
797                                   key, MODEST_ACCOUNT_PASSWORD, TRUE);
798         */
799 }
800
801 static void
802 modest_tny_account_store_finalize (GObject *obj)
803 {
804         ModestTnyAccountStore *self        = MODEST_TNY_ACCOUNT_STORE(obj);
805         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
806
807         g_free (priv->cache_dir);
808         priv->cache_dir = NULL;
809         
810         if (priv->password_hash) {
811                 g_hash_table_destroy (priv->password_hash);
812                 priv->password_hash = NULL;
813         }
814
815         if (priv->account_settings_dialog_hash) {
816                 g_hash_table_destroy (priv->account_settings_dialog_hash);
817                 priv->account_settings_dialog_hash = NULL;
818         }
819
820         if (priv->outbox_of_transport) {
821                 g_hash_table_destroy (priv->outbox_of_transport);
822                 priv->outbox_of_transport = NULL;
823         }
824
825         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
826         priv->sighandlers = NULL;       
827
828         if (priv->account_mgr) {
829                 g_object_unref (G_OBJECT(priv->account_mgr));
830                 priv->account_mgr = NULL;
831         }
832
833         if (priv->device) {
834                 g_object_unref (G_OBJECT(priv->device));
835                 priv->device = NULL;
836         }
837
838         /* Destroy all accounts. Disconnect all accounts before they are destroyed */
839         if (priv->store_accounts) {
840                 tny_list_foreach (priv->store_accounts, (GFunc)account_disconnect, NULL);
841                 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
842                 g_object_unref (priv->store_accounts);
843                 priv->store_accounts = NULL;
844         }
845         
846         if (priv->transport_accounts) {
847                 tny_list_foreach (priv->transport_accounts, (GFunc)account_disconnect, NULL);
848                 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
849                 g_object_unref (priv->transport_accounts);
850                 priv->transport_accounts = NULL;
851         }
852
853         if (priv->store_accounts_outboxes) {
854                 g_object_unref (priv->store_accounts_outboxes);
855                 priv->store_accounts_outboxes = NULL;
856         }
857                 
858         if (priv->session) {
859                 camel_object_unref (CAMEL_OBJECT(priv->session));
860                 priv->session = NULL;
861         }
862
863         G_OBJECT_CLASS(parent_class)->finalize (obj);
864 }
865
866 gboolean 
867 volume_path_is_mounted (const gchar* path)
868 {
869         g_return_val_if_fail (path, FALSE);
870
871         gboolean result = FALSE;
872         gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
873         g_return_val_if_fail (path_as_uri, FALSE);
874
875         /* Get the monitor singleton: */
876         GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
877
878         /* This seems like a simpler way to do this, but it returns a   
879          * GnomeVFSVolume even if the drive is not mounted: */
880         /*
881         GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor, 
882                 MODEST_MCC1_VOLUMEPATH);
883         if (volume) {
884                 gnome_vfs_volume_unref(volume);
885         }
886         */
887
888         /* Get the mounted volumes from the monitor: */
889         GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
890         GList *iter = list;
891         for (iter = list; iter; iter = g_list_next (iter)) {
892                 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
893                 if (volume) {
894                         /*
895                         char *display_name = 
896                                 gnome_vfs_volume_get_display_name (volume);
897                         printf ("volume display name=%s\n", display_name);
898                         g_free (display_name);
899                         */
900                         
901                         char *uri = 
902                                 gnome_vfs_volume_get_activation_uri (volume);
903                         /* printf ("  uri=%s\n", uri); */
904                         if (uri && (strcmp (uri, path_as_uri) == 0))
905                                 result = TRUE;
906
907                         g_free (uri);
908
909                         gnome_vfs_volume_unref (volume);
910                 }
911         }
912
913         g_list_free (list);
914
915         g_free (path_as_uri);
916
917         return result;
918 }
919
920 ModestTnyAccountStore*
921 modest_tny_account_store_new (ModestAccountMgr *account_mgr, 
922                               TnyDevice *device) 
923 {
924         GObject *obj;
925         ModestTnyAccountStorePrivate *priv;
926         TnyAccount *local_account = NULL;
927         
928         g_return_val_if_fail (account_mgr, NULL);
929         g_return_val_if_fail (device, NULL);
930
931         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
932         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
933
934         priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
935         priv->device = g_object_ref (device);
936         
937         priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
938         if (!priv->session) {
939                 g_warning ("failed to get TnySessionCamel");
940                 return NULL;
941         }
942
943         /* Set the ui locker */ 
944         tny_session_camel_set_ui_locker (priv->session,  tny_gtk_lockable_new ());
945         
946         /* Connect signals */
947         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
948                                                          G_OBJECT(account_mgr), "account_inserted",
949                                                          G_CALLBACK (on_account_inserted), obj);
950         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
951                                                          G_OBJECT(account_mgr), "account_changed",
952                                                          G_CALLBACK (on_account_changed), obj);
953         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
954                                                          G_OBJECT(account_mgr), "account_removed",
955                                                          G_CALLBACK (on_account_removed), obj);
956
957         /* Create the lists of accounts */
958         priv->store_accounts = tny_simple_list_new ();
959         priv->transport_accounts = tny_simple_list_new ();
960         priv->store_accounts_outboxes = tny_simple_list_new ();
961
962         /* Create the local folders account */
963         local_account = 
964                 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
965         tny_list_append (priv->store_accounts, G_OBJECT(local_account));
966         g_object_unref (local_account); 
967
968         /* Add the other remote accounts. Do this after adding the
969            local account, because we need to add our outboxes to the
970            global OUTBOX hosted in the local account */
971         add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
972         
973         /* Add connection-specific transport accounts if there are any
974            accounts available */
975         if (!only_local_accounts (MODEST_TNY_ACCOUNT_STORE(obj)))
976                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
977         
978         /* This is a singleton, so it does not need to be unrefed. */
979         if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
980                 /* It is mounted: */
981                 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */); 
982         }
983         
984         return MODEST_TNY_ACCOUNT_STORE(obj);
985 }
986
987 static void
988 modest_tny_account_store_get_accounts  (TnyAccountStore *self, 
989                                         TnyList *list,
990                                         TnyGetAccountsRequestType request_type)
991 {
992         ModestTnyAccountStorePrivate *priv;
993         
994         g_return_if_fail (self);
995         g_return_if_fail (TNY_IS_LIST(list));
996         
997         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
998         
999         switch (request_type) {
1000         case TNY_ACCOUNT_STORE_BOTH:
1001                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1002                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1003                 break;
1004         case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
1005                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1006                 break;
1007         case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
1008                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1009                 break;
1010         default:
1011                 g_return_if_reached ();
1012         }
1013
1014         /* Initialize session. Why do we need this ??? */
1015         tny_session_camel_set_initialized (priv->session);
1016 }
1017
1018
1019 static const gchar*
1020 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
1021 {
1022         ModestTnyAccountStorePrivate *priv;
1023         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1024         
1025         if (!priv->cache_dir)
1026                 priv->cache_dir = g_build_filename (g_get_home_dir(), 
1027                                                     MODEST_DIR, MODEST_CACHE_DIR, NULL);
1028         return priv->cache_dir;
1029 }
1030
1031
1032 /*
1033  * callers need to unref
1034  */
1035 static TnyDevice*
1036 modest_tny_account_store_get_device (TnyAccountStore *self)
1037 {
1038         ModestTnyAccountStorePrivate *priv;
1039
1040         g_return_val_if_fail (self, NULL);
1041         
1042         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1043         
1044         if (priv->device) 
1045                 return g_object_ref (G_OBJECT(priv->device));
1046         else
1047                 return NULL;
1048 }
1049
1050
1051 static TnyAccount*
1052 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1053 {
1054         return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self), 
1055                                                             MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1056                                                             url_string);
1057 }
1058
1059
1060
1061 static gboolean
1062 modest_tny_account_store_alert (TnyAccountStore *self, 
1063                                 TnyAccount *account, 
1064                                 TnyAlertType type,
1065                                 gboolean question, 
1066                                 GError *error)
1067 {
1068         ModestTransportStoreProtocol proto =
1069                 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN; 
1070         const gchar* server_name = "";
1071         gchar *prompt = NULL;
1072         gboolean retval;
1073
1074         /* NOTE: account may be NULL in some cases */
1075         g_return_val_if_fail (error, FALSE);
1076         
1077         /* Get the server name: */
1078         if (account) {
1079                 server_name = tny_account_get_hostname (account);
1080                 const gchar *proto_name = tny_account_get_proto (account);
1081                 if (proto_name)
1082                         proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1083                 else {
1084                         g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__, 
1085                                   tny_account_get_id (account));
1086                         return FALSE;
1087                 }
1088         }
1089
1090         switch (error->code) {
1091         case TNY_SYSTEM_ERROR_CANCEL:
1092                 /* Don't show waste the user's time by showing him a dialog telling 
1093                  * him that he has just cancelled something: */
1094                 return TRUE;
1095
1096         case TNY_SERVICE_ERROR_PROTOCOL:
1097                 /* Like a BAD from IMAP (protocol error) */
1098         case TNY_SERVICE_ERROR_LOST_CONNECTION:
1099                 /* Lost the connection with the service */
1100         case TNY_SERVICE_ERROR_UNAVAILABLE:
1101                 /* You must be working online for this operation */
1102         case TNY_SERVICE_ERROR_CONNECT:
1103                 switch (proto) {
1104                 case MODEST_PROTOCOL_STORE_POP:
1105                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1106                                                   server_name);
1107                         break;
1108                 case MODEST_PROTOCOL_STORE_IMAP:
1109                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1110                                                   server_name);
1111                         break;
1112                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1113                         prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1114                                                   server_name);
1115                         break;
1116                 default:
1117                         g_return_val_if_reached (FALSE);
1118                 }
1119                 break;
1120                 
1121         case TNY_SERVICE_ERROR_AUTHENTICATE:
1122                 /* It seems that there's no better error to show with
1123                  * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1124                  * may appear if there's a timeout during auth */
1125                 switch (proto) {
1126                 case MODEST_PROTOCOL_STORE_POP:
1127                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1128                                                   server_name);
1129                         break;
1130                 case MODEST_PROTOCOL_STORE_IMAP:
1131                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1132                                                   server_name);
1133                         break;
1134                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1135                         prompt = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
1136                                                   server_name);
1137                         break;
1138                 default:
1139                         g_return_val_if_reached (FALSE);
1140                 }
1141                 break;
1142                         
1143         case TNY_SERVICE_ERROR_CERTIFICATE:
1144                 /* We'll show the proper dialog later */
1145                 break;
1146
1147         case TNY_SYSTEM_ERROR_MEMORY:
1148                 /* Can't allocate memory for this operation */
1149
1150         case TNY_SERVICE_ERROR_UNKNOWN: 
1151                 return FALSE;                   
1152         default:
1153                 g_debug ("Unexpected error %d", error->code);
1154                 g_return_val_if_reached (FALSE);
1155         }
1156         
1157
1158         if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1159                 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1160                                                                               error->message);
1161         else if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE) {
1162                 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1163
1164                 /* Show the account dialog if it was wrong */
1165                 if (error->code == TNY_SERVICE_ERROR_CONNECT || 
1166                     error->code == TNY_SERVICE_ERROR_AUTHENTICATE)
1167                         show_wrong_password_dialog (account);
1168
1169                 retval = TRUE;
1170         }
1171
1172         
1173         if (prompt)
1174                 g_free (prompt);
1175         
1176         return retval;
1177 }
1178
1179
1180 static void
1181 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1182 {
1183         TnyAccountStoreIface *klass;
1184
1185         g_return_if_fail (g);
1186
1187         klass = (TnyAccountStoreIface *)g;
1188
1189         klass->get_accounts =
1190                 modest_tny_account_store_get_accounts;
1191         klass->get_cache_dir =
1192                 modest_tny_account_store_get_cache_dir;
1193         klass->get_device =
1194                 modest_tny_account_store_get_device;
1195         klass->alert =
1196                 modest_tny_account_store_alert;
1197         klass->find_account =
1198                 modest_tny_account_store_find_account_by_url;
1199 }
1200
1201 void
1202 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1203                                             ModestTnyGetPassFunc func)
1204 {
1205         /* not implemented, we use signals */
1206         g_printerr ("modest: set_get_pass_func not implemented\n");
1207 }
1208
1209 TnySessionCamel*
1210 modest_tny_account_store_get_session  (TnyAccountStore *self)
1211 {
1212         g_return_val_if_fail (self, NULL);
1213         return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1214 }
1215
1216 static TnyAccount*
1217 get_tny_account_by (TnyList *accounts,
1218                     ModestTnyAccountStoreQueryType type,
1219                     const gchar *str)
1220 {
1221         TnyIterator *iter = NULL;
1222         gboolean found = FALSE;
1223         TnyAccount *retval = NULL;
1224
1225         g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1226
1227         if (tny_list_get_length(accounts) == 0) {
1228                 g_warning ("%s: account list is empty", __FUNCTION__);
1229                 return NULL;
1230         }
1231         
1232         iter = tny_list_create_iterator (accounts);
1233         while (!tny_iterator_is_done (iter) && !found) {
1234                 TnyAccount *tmp_account = NULL;
1235                 const gchar *val = NULL;
1236
1237                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1238                 if (!TNY_IS_ACCOUNT(tmp_account)) {
1239                         g_warning ("%s: not a valid account", __FUNCTION__);
1240                         tmp_account = NULL;
1241                         break;
1242                 }
1243
1244                 switch (type) {
1245                 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1246                         val = tny_account_get_id (tmp_account);
1247                         break;
1248                 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1249                         val = tny_account_get_url_string (tmp_account);
1250                         break;
1251                 }
1252                 
1253                 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL && 
1254                     tny_account_matches_url_string (tmp_account, str)) {
1255                         retval = g_object_ref (tmp_account);
1256                         found = TRUE;
1257                 } else {
1258                         if (val && str && strcmp (val, str) == 0) {
1259                                 retval = g_object_ref (tmp_account);
1260                                 found = TRUE;
1261                         }
1262                 }
1263                 g_object_unref (tmp_account);
1264                 tny_iterator_next (iter);
1265         }
1266         g_object_unref (iter);
1267
1268         return retval;
1269 }
1270
1271 TnyAccount*
1272 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self, 
1273                                              ModestTnyAccountStoreQueryType type,
1274                                              const gchar *str)
1275 {
1276         TnyAccount *account = NULL;
1277         ModestTnyAccountStorePrivate *priv;     
1278         
1279         g_return_val_if_fail (self, NULL);
1280         g_return_val_if_fail (str, NULL);
1281         
1282         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1283         
1284         /* Search in store accounts */
1285         account = get_tny_account_by (priv->store_accounts, type, str);
1286
1287         /* If we already found something, no need to search the transport accounts */
1288         if (!account) {
1289                 account = get_tny_account_by (priv->transport_accounts, type, str);
1290
1291                 /* If we already found something, no need to search the
1292                    per-account outbox accounts */
1293                 if (!account)
1294                         account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1295         }
1296
1297         /* Warn if nothing was found. This is generally unusual. */
1298         if (!account) {
1299                 g_warning("%s: Failed to find account with %s=%s\n", 
1300                           __FUNCTION__, 
1301                           (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",                     
1302                           str);
1303         }
1304
1305         /* Returns a new reference to the account if found */   
1306         return account;
1307 }
1308
1309
1310 TnyAccount*
1311 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1312                                              const gchar *account_name,
1313                                              TnyAccountType type)
1314 {
1315         ModestTnyAccountStorePrivate *priv = NULL;
1316         TnyAccount *retval = NULL;
1317         TnyList *account_list = NULL;
1318         TnyIterator *iter = NULL;
1319         gboolean found;
1320
1321         g_return_val_if_fail (self, NULL);
1322         g_return_val_if_fail (account_name, NULL);
1323         g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || 
1324                               type == TNY_ACCOUNT_TYPE_TRANSPORT,
1325                               NULL);
1326         
1327         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1328
1329         account_list = (type == TNY_ACCOUNT_TYPE_STORE) ? 
1330                 priv->store_accounts : 
1331                 priv->transport_accounts;
1332
1333         if (!account_list) {
1334                 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__, 
1335                         (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1336                 return NULL;
1337         }
1338         
1339         /* Look for the server account */
1340         found = FALSE;
1341         iter = tny_list_create_iterator (account_list);
1342         while (!tny_iterator_is_done (iter) && !found) {
1343                 const gchar *modest_acc_name;
1344                 TnyAccount *tmp_account;
1345
1346                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1347                 modest_acc_name = 
1348                         modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1349                 
1350                 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1351                         found = TRUE;
1352                         retval = g_object_ref (tmp_account);
1353                 }
1354                 /* Free and continue */
1355                 g_object_unref (tmp_account);
1356                 tny_iterator_next (iter);
1357         }
1358         g_object_unref (iter);
1359
1360         if (!found) {
1361                 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1362                             "Number of server accounts of this type=%d\n", __FUNCTION__,
1363                             (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1364                             account_name, tny_list_get_length (account_list));
1365         }
1366
1367         /* Returns a new reference */
1368         return retval;
1369 }
1370
1371 TnyAccount*
1372 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1373         ModestTnyAccountStore *self, const gchar *account_name)
1374 {
1375         TnyDevice *device;
1376
1377         g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1378         g_return_val_if_fail (account_name, NULL);
1379
1380         /* Get the current connection: */
1381         device = modest_runtime_get_device ();
1382
1383         if (!device) {
1384                 g_warning ("%s: could not get device", __FUNCTION__);
1385                 return NULL;
1386         }
1387                 
1388         if (!tny_device_is_online (device))
1389                 return NULL;
1390         
1391 #ifdef MODEST_HAVE_CONIC
1392         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1393         
1394         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1395         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1396         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1397         if (!iap_id)
1398                 return NULL;
1399                 
1400         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1401         if (!connection)
1402                 return NULL;
1403                 
1404         const gchar *connection_id = con_ic_iap_get_id (connection);
1405         /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1406         if (!connection_id)
1407                 return NULL;
1408         
1409         /*  Get the connection-specific transport acccount, if any: */
1410         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1411
1412         /* Check if this account has connection-specific SMTP enabled */
1413         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1414                 return NULL;
1415         }
1416
1417         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1418                 connection_id);
1419
1420         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1421         if (!server_account_name) {
1422                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1423         }
1424                 
1425         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1426                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1427                                                                            server_account_name);
1428
1429         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1430         g_free (server_account_name);   
1431
1432         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1433         g_object_unref (connection);
1434         
1435         return account;
1436 #else
1437         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1438 #endif /* MODEST_HAVE_CONIC */
1439 }
1440
1441                                                                  
1442 TnyAccount*
1443 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1444                                                                     const gchar *account_name)
1445 {
1446         g_return_val_if_fail (self, NULL);
1447         g_return_val_if_fail (account_name, NULL);
1448
1449         if (!account_name || !self)
1450                 return NULL;
1451         
1452         /*  Get the connection-specific transport acccount, if any: */
1453         /* Note: This gives us a reference: */
1454         TnyAccount *account =
1455                 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1456                         
1457         /* If there is no connection-specific transport account (the common case), 
1458          * just get the regular transport account: */
1459         if (!account) {
1460                 /* The special local folders don't have transport accounts. */
1461                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1462                         account = NULL;
1463                 else {
1464                         /* Note: This gives us a reference: */
1465                         account = modest_tny_account_store_get_server_account (self, account_name, 
1466                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1467                 }
1468         }
1469                         
1470         /* returns a reference. */     
1471         return account;
1472 }
1473
1474 TnyAccount*
1475 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1476 {
1477         TnyAccount *account = NULL;
1478         ModestTnyAccountStorePrivate *priv;
1479         TnyIterator *iter;
1480         gboolean found;
1481
1482         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1483         
1484         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1485
1486         found = FALSE;
1487         iter = tny_list_create_iterator (priv->store_accounts);
1488         while (!tny_iterator_is_done (iter) && !found) {
1489                 TnyAccount *tmp_account;
1490
1491                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1492                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1493                         account = g_object_ref (tmp_account);
1494                         found = TRUE;
1495                 }
1496                 g_object_unref (tmp_account);
1497                 tny_iterator_next (iter);
1498         }
1499         g_object_unref (iter);
1500
1501         /* Returns a new reference to the account */
1502         return account;
1503 }
1504
1505 TnyAccount*
1506 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1507 {
1508         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1509         
1510         /* New reference */
1511         return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1512                                                             MODEST_MMC_ACCOUNT_ID);
1513
1514 }
1515
1516 /*********************************************************************************/
1517 static void
1518 add_existing_accounts (ModestTnyAccountStore *self)
1519 {
1520         GSList *account_names = NULL, *iter = NULL;
1521         ModestTnyAccountStorePrivate *priv = NULL;
1522         
1523         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1524
1525         /* These are account names, not server_account names */
1526         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1527
1528         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1529                 const gchar *account_name = (const gchar*) iter->data;
1530                 
1531                 /* Insert all enabled accounts without notifying */
1532                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1533                         insert_account (self, account_name, FALSE);
1534         }
1535         modest_account_mgr_free_account_names (account_names);
1536 }
1537
1538 static void 
1539 connection_status_changed (TnyAccount *account, 
1540                            TnyConnectionStatus status, 
1541                            gpointer data)
1542 {
1543         /* We do this here and not in the connection policy because we
1544            don't want to do it for every account, just for the
1545            accounts that are interactively added when modest is
1546            runnning */
1547         if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1548                 const gchar *account_name;
1549                 ModestWindow *main_window;
1550                 ModestTnyAccountStorePrivate *priv = NULL;
1551                 
1552                 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1553
1554                 /* Remove this handler */
1555                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, 
1556                                                                   G_OBJECT (account),
1557                                                                   "connection_status_changed");
1558
1559                 /* Perform a send receive */
1560                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1561                 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1562                 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, TRUE, main_window);
1563         }
1564 }
1565
1566 static TnyAccount*
1567 create_tny_account (ModestTnyAccountStore *self,
1568                     const gchar *name,
1569                     TnyAccountType type,
1570                     gboolean notify)
1571 {
1572         TnyAccount *account = NULL;
1573         ModestTnyAccountStorePrivate *priv = NULL;
1574         
1575         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1576
1577         account = modest_tny_account_new_from_account (priv->account_mgr,
1578                                                        name, type, 
1579                                                        priv->session,
1580                                                        get_password,
1581                                                        forget_password);
1582
1583         if (account) {
1584                 /* Forget any cached password for the account, so that
1585                    we use a new account if any */
1586                 forget_password_in_memory (self, tny_account_get_id (account));
1587
1588                 /* Install a signal handler that will refresh the
1589                    account the first time it becomes online. Do this
1590                    only if we're adding a new account while the
1591                    program is running (we do not want to do this
1592                    allways) */
1593                 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1594                         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, 
1595                                                                        G_OBJECT (account), 
1596                                                                        "connection_status_changed",
1597                                                                        G_CALLBACK (connection_status_changed),
1598                                                                        self);
1599
1600                 /* Set the account store */
1601                 g_object_set_data (G_OBJECT(account), "account_store", self);
1602         } else {
1603                 g_printerr ("modest: failed to create account for %s\n", name);
1604         }
1605
1606         return account;
1607 }
1608
1609 typedef struct _AddOutboxInfo {
1610         ModestTnyAccountStore *account_store;
1611         TnyAccount *transport_account;
1612 } AddOutboxInfo;
1613
1614 static void
1615 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1616                                                                    gboolean cancelled,
1617                                                                    TnyList *list,
1618                                                                    GError *err,
1619                                                                    gpointer userdata)
1620 {
1621         TnyIterator *iter_folders;
1622         TnyFolder *per_account_outbox;
1623         TnyAccount *local_account = NULL;
1624         AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1625         ModestTnyAccountStorePrivate *priv = NULL;
1626         ModestTnyAccountStore *self;
1627
1628         self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1629         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1630         
1631         /* Note that this could happen if there is not enough space
1632            available on disk, then the outbox folder could not be
1633            created */
1634         if (tny_list_get_length (list) != 1) {
1635                 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1636                            tny_list_get_length (list));
1637                 goto frees;
1638         }
1639                         
1640         iter_folders = tny_list_create_iterator (list);
1641         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1642         g_object_unref (iter_folders);
1643         g_object_unref (list);
1644
1645         /* Add the outbox of the new per-account-local-outbox account
1646            to the global local merged OUTBOX of the local folders
1647            account */
1648         local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1649         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1650                                                                per_account_outbox);
1651         /* Add the pair to the hash table */
1652         g_hash_table_insert (priv->outbox_of_transport,
1653                              info->transport_account,
1654                              per_account_outbox);
1655         
1656         /* Notify that the local account changed */
1657         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1658         g_object_unref (local_account);
1659         g_object_unref (per_account_outbox);
1660
1661  frees:
1662         g_object_unref (info->transport_account);
1663         g_slice_free (AddOutboxInfo, info);
1664 }
1665                                                                    
1666
1667 static void
1668 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1669                                                     const gchar *account_name,
1670                                                     TnyAccount *transport_account)
1671 {
1672         TnyList *folders = NULL;
1673         TnyAccount *account_outbox = NULL;
1674         ModestTnyAccountStorePrivate *priv = NULL;
1675         AddOutboxInfo *info;
1676
1677         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1678
1679         /* Create per account local outbox */
1680         account_outbox = 
1681                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1682                                                                             account_name, 
1683                                                                             priv->session);
1684
1685         if (!G_IS_OBJECT (account_outbox)) {
1686                 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1687                 return;
1688         }
1689
1690         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1691         
1692         /* Get the outbox folder */
1693         folders = tny_simple_list_new ();
1694         info = g_slice_new0 (AddOutboxInfo);
1695         info->account_store = self;
1696         info->transport_account = g_object_ref (transport_account);
1697         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL, 
1698                                             add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1699         g_object_unref (account_outbox);
1700 }
1701
1702 /*
1703  * This function will be used for both adding new accounts and for the
1704  * initialization. In the initialization we do not want to emit
1705  * signals so notify will be FALSE, in the case of account additions
1706  * we do want to notify the observers
1707  */
1708 static void
1709 insert_account (ModestTnyAccountStore *self,
1710                 const gchar *account,
1711                 gboolean notify)
1712 {
1713         ModestTnyAccountStorePrivate *priv = NULL;
1714         TnyAccount *store_account = NULL, *transport_account = NULL;
1715         
1716         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1717
1718         /* Get the server and the transport account */
1719         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1720         if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1721                 g_warning ("%s: failed to create store account", __FUNCTION__);
1722                 return;
1723         }
1724
1725         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1726         if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1727                 g_warning ("%s: failed to create transport account", __FUNCTION__);
1728                 g_object_unref (store_account);
1729                 return;
1730         }
1731
1732         /* Add accounts to the lists */
1733         tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1734         tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1735         
1736         /* Create a new pseudo-account with an outbox for this
1737            transport account and add it to the global outbox
1738            in the local account */
1739         add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);  
1740
1741         /* Notify the observers. We do it after everything is
1742            created */
1743         if (notify) {
1744                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);   
1745                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1746         }
1747
1748         /* Frees */
1749         g_object_unref (store_account);
1750         g_object_unref (transport_account);
1751 }
1752
1753 static gboolean
1754 only_local_accounts (ModestTnyAccountStore *self)
1755 {
1756         ModestTnyAccountStorePrivate *priv = NULL;
1757         gboolean only_local = TRUE;
1758         TnyIterator *iter;
1759
1760         /* Check if this is the first remote account we add */
1761         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1762         iter = tny_list_create_iterator (priv->store_accounts);
1763
1764         while (!tny_iterator_is_done (iter) && only_local) {
1765                 TnyAccount *account = (TnyAccount *) tny_iterator_get_current (iter);
1766                 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1767                         only_local = FALSE;
1768                 g_object_unref (account);
1769                 tny_iterator_next (iter);
1770         }
1771         g_object_unref (iter);
1772
1773         return only_local;
1774 }
1775
1776 static void
1777 on_account_inserted (ModestAccountMgr *acc_mgr, 
1778                      const gchar *account,
1779                      gpointer user_data)
1780 {
1781         gboolean add_specific;
1782
1783         add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1784                 
1785         /* Insert the account and notify the observers */
1786         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1787
1788         /* If it's the first remote account then add the connection
1789            specific SMTP servers as well */
1790         if (add_specific)
1791                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1792
1793 }
1794
1795 /* This is the callback of the tny_camel_account_set_online called in
1796    on_account_removed to disconnect the account */
1797 static void
1798 on_account_disconnect_when_removing (TnyCamelAccount *account, 
1799                                      gboolean canceled, 
1800                                      GError *err, 
1801                                      gpointer user_data)
1802 {
1803         ModestTnyAccountStore *self;
1804         ModestTnyAccountStorePrivate *priv;
1805
1806         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1807         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1808
1809         /* Remove the connection-status-changed handler if it's still there */
1810         if (modest_signal_mgr_is_connected (priv->sighandlers, 
1811                                             G_OBJECT (account),
1812                                             "connection_status_changed")) {
1813                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, 
1814                                                                   G_OBJECT (account),
1815                                                                   "connection_status_changed");
1816         }
1817
1818         /* Cancel all pending operations */
1819         tny_account_cancel (TNY_ACCOUNT (account));
1820         
1821         /* Unref the extra reference added by get_server_account */
1822         g_object_unref (account);
1823
1824         /* Clear the cache if it's an store account */
1825         if (TNY_IS_STORE_ACCOUNT (account)) {
1826                 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1827         } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1828                 ModestTnySendQueue* send_queue;
1829                 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1830                 if (TNY_IS_SEND_QUEUE (send_queue)) {
1831                         if (modest_tny_send_queue_sending_in_progress (send_queue))
1832                                 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1833                                                        TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE, 
1834                                                        NULL);
1835                         modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1836                 }
1837         }
1838 }
1839
1840 /*
1841  * We use this one for both removing "normal" and "connection
1842  * specific" transport accounts
1843  */
1844 static void
1845 remove_transport_account (ModestTnyAccountStore *self,
1846                           TnyTransportAccount *transport_account)
1847 {
1848         ModestTnyAccountStorePrivate *priv;
1849         TnyFolder *outbox = NULL;
1850         
1851         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1852
1853         /* Remove it from the list of accounts and notify the
1854            observers. Do not need to wait for account
1855            disconnection */
1856         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1857         tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1858                 
1859         /* Remove the OUTBOX of the account from the global outbox */
1860         outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1861
1862         if (TNY_IS_FOLDER (outbox)) {
1863                 TnyAccount *local_account = NULL;
1864                 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1865
1866                 if (outbox_account) {
1867                         tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1868                         /* Remove existing emails to send */
1869                         tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1870                         g_object_unref (outbox_account);
1871                 }
1872
1873                 local_account = modest_tny_account_store_get_local_folders_account (self);
1874                 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1875                                                                             outbox);
1876
1877                 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1878
1879                 /* Notify the change in the local account */
1880                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1881                 g_object_unref (local_account);
1882         } else {
1883                 g_warning ("Removing a transport account that has no outbox");
1884         }
1885
1886         /* Cancel all pending operations */
1887         tny_account_cancel (TNY_ACCOUNT (transport_account));
1888
1889         /* Disconnect and notify the observers. The callback will free the reference */
1890         tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1891                                       on_account_disconnect_when_removing, self);
1892 }
1893
1894 static void
1895 on_account_removed (ModestAccountMgr *acc_mgr, 
1896                     const gchar *account,
1897                     gpointer user_data)
1898 {
1899         TnyAccount *store_account = NULL, *transport_account = NULL;
1900         ModestTnyAccountStore *self;
1901         ModestTnyAccountStorePrivate *priv;
1902         
1903         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1904         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1905
1906         /* Get the server and the transport account */
1907         store_account = 
1908                 modest_tny_account_store_get_server_account (self, account, 
1909                                                              TNY_ACCOUNT_TYPE_STORE);
1910         transport_account = 
1911                 modest_tny_account_store_get_server_account (self, account,
1912                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1913         
1914         /* If there was any problem creating the account, for example,
1915            with the configuration system this could not exist */
1916         if (TNY_IS_STORE_ACCOUNT(store_account)) {
1917                 /* Forget any cached password for the account */
1918                 forget_password_in_memory (self, tny_account_get_id (store_account));
1919
1920                 /* Remove it from the list of accounts and notify the
1921                    observers. Do not need to wait for account
1922                    disconnection */
1923                 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1924                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1925
1926                 /* Cancel all pending operations */
1927                 tny_account_cancel (TNY_ACCOUNT (store_account));
1928
1929                 /* Disconnect before deleting the cache, because the
1930                    disconnection will rewrite the cache to the
1931                    disk */
1932                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1933                                               on_account_disconnect_when_removing, self);
1934         } else {
1935                 g_warning ("%s: no store account for account %s\n", 
1936                            __FUNCTION__, account);
1937         }
1938
1939         /* If there was any problem creating the account, for example,
1940            with the configuration system this could not exist */
1941         if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1942
1943                 /* Forget any cached password for the account */
1944                 forget_password_in_memory (self, tny_account_get_id (transport_account));
1945
1946                 /* Remove transport account. It'll free the reference
1947                    added by get_server_account */
1948                 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1949         } else {
1950                 g_warning ("%s: no transport account for account %s\n", 
1951                            __FUNCTION__, account);
1952         }
1953
1954         /* If there are no more user accounts then delete the
1955            transport specific SMTP servers */
1956         if (only_local_accounts (self))
1957                 remove_connection_specific_transport_accounts (self);
1958 }
1959
1960 TnyTransportAccount *
1961 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1962                                                                     const gchar *name)
1963 {
1964         ModestTnyAccountStorePrivate *priv = NULL;
1965         TnyAccount * tny_account = NULL;
1966
1967         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1968
1969         /* Add the account: */
1970         tny_account = 
1971                 modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1972                                                                  priv->session, 
1973                                                                  name,
1974                                                                  get_password,
1975                                                                  forget_password);
1976         if (tny_account) {
1977                 g_object_set_data (G_OBJECT(tny_account), 
1978                                    "account_store", 
1979                                    (gpointer)self);
1980                 g_object_set_data (G_OBJECT(tny_account), 
1981                                    "connection_specific", 
1982                                    GINT_TO_POINTER (TRUE));
1983                 
1984                 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1985                 add_outbox_from_transport_account_to_global_outbox (self, 
1986                                                                     name, 
1987                                                                     tny_account);
1988                 
1989         } else
1990                 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1991                             name);
1992
1993         return TNY_TRANSPORT_ACCOUNT (tny_account);
1994 }
1995
1996
1997 static void
1998 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1999 {
2000         ModestTnyAccountStorePrivate *priv = NULL;
2001         GSList *list_specifics = NULL, *iter = NULL;
2002         GError *err = NULL;
2003
2004         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2005
2006         list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2007                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2008                                                MODEST_CONF_VALUE_STRING, &err);
2009         if (err) {
2010                 g_error_free (err);
2011                 g_return_if_reached ();
2012         }
2013                                 
2014         /* Look at each connection-specific transport account for the 
2015          * modest account: */
2016         iter = list_specifics;
2017         while (iter) {
2018                 /* The list alternates between the connection name and the transport name: */
2019                 iter = g_slist_next (iter);
2020                 if (iter) {
2021                         const gchar* transport_account_name = (const gchar*) (iter->data);
2022                         TnyTransportAccount * account = NULL;
2023                         account = modest_tny_account_store_new_connection_specific_transport_account (
2024                                 self, transport_account_name);
2025                         if (account)
2026                                 g_object_unref (account);
2027                 }                               
2028                 iter = g_slist_next (iter);
2029         }
2030 }
2031
2032 static void
2033 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2034 {
2035         ModestTnyAccountStorePrivate *priv = NULL;
2036         GSList *list_specifics = NULL, *iter = NULL;
2037         GError *err = NULL;
2038
2039         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2040
2041         err = NULL;
2042         list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2043                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2044                                                MODEST_CONF_VALUE_STRING, &err);
2045         if (err) {
2046                 g_error_free (err);
2047                 g_return_if_reached ();
2048         }
2049                                 
2050         /* Look at each connection-specific transport account for the 
2051          * modest account: */
2052         iter = list_specifics;
2053         while (iter) {
2054                 /* The list alternates between the connection name and the transport name: */
2055                 iter = g_slist_next (iter);
2056                 if (iter) {
2057                         const gchar* transport_account_name = (const gchar*) (iter->data);
2058                         TnyAccount * account;
2059                         account = modest_tny_account_store_get_server_account (self,
2060                                                                                transport_account_name,
2061                                                                                TNY_ACCOUNT_TYPE_TRANSPORT);
2062
2063                         /* the call will free the reference */
2064                         if (account)
2065                                 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2066                 }                               
2067                 iter = g_slist_next (iter);
2068         }
2069 }
2070
2071
2072 TnyMsg *
2073 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self, 
2074                                                const gchar *uri,
2075                                                TnyAccount **ac_out)
2076 {
2077         TnyIterator *acc_iter;
2078         ModestTnyAccountStorePrivate *priv;
2079         TnyMsg *msg = NULL;
2080         TnyAccount *msg_account = NULL;
2081
2082         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2083         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2084
2085         acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2086         while (!msg && !tny_iterator_is_done (acc_iter)) {
2087                 TnyList *folders = tny_simple_list_new ();
2088                 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2089                 TnyIterator *folders_iter = NULL;
2090
2091                 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
2092                 folders_iter = tny_list_create_iterator (folders);
2093
2094                 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2095                         TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2096                         msg = tny_folder_find_msg (folder, uri, NULL);
2097
2098                         if (msg)
2099                                 msg_account = g_object_ref (account);
2100
2101                         g_object_unref (folder);
2102                         tny_iterator_next (folders_iter);
2103                 }
2104                 g_object_unref (folders_iter);
2105
2106                 g_object_unref (folders);
2107                 g_object_unref (account);
2108                 tny_iterator_next (acc_iter);
2109         }
2110
2111         g_object_unref (acc_iter);
2112
2113         if (ac_out != NULL)
2114                 *ac_out = msg_account;
2115
2116         return msg;
2117 }
2118
2119 TnyTransportAccount *
2120 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2121 {
2122         TnyIterator *acc_iter;
2123         ModestTnyAccountStorePrivate *priv;
2124         TnyTransportAccount *header_acc = NULL;
2125         gchar *msg_id;
2126
2127         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2128         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2129         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2130         msg_id = modest_tny_send_queue_get_msg_id (header);
2131         acc_iter = tny_list_create_iterator (priv->transport_accounts);
2132         while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2133                 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2134                 ModestTnySendQueue *send_queue;
2135                 ModestTnySendQueueStatus status;
2136                 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2137                 if (TNY_IS_SEND_QUEUE (send_queue)) {
2138                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2139                         if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2140                                 header_acc = g_object_ref(account);
2141                 }
2142                 g_object_unref (account);
2143                 tny_iterator_next (acc_iter);
2144         }
2145         g_object_unref(acc_iter);
2146         g_free (msg_id);
2147
2148         /* New reference */
2149         return header_acc;
2150 }
2151
2152 GtkWidget *
2153 modest_tny_account_store_show_account_settings_dialog (ModestTnyAccountStore *self,
2154                                                       const gchar *account_name)
2155 {
2156         ModestTnyAccountStorePrivate *priv;
2157         gpointer dialog_as_gpointer = NULL;
2158         gboolean found;
2159
2160         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2161         found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
2162                                               account_name, NULL, (gpointer*)&dialog_as_gpointer);
2163
2164         if (found) {
2165                 modest_account_settings_dialog_check_allow_changes ((ModestAccountSettingsDialog *) dialog_as_gpointer);
2166                 return (GtkWidget *) dialog_as_gpointer;
2167         } else {
2168                 ModestAccountSettings *settings;
2169                 GtkWidget *dialog;
2170                 dialog = (GtkWidget *) modest_account_settings_dialog_new ();
2171                 settings = modest_account_mgr_load_account_settings (priv->account_mgr, account_name);
2172                 modest_account_settings_dialog_save_password (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2173                 modest_account_settings_dialog_set_account (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog), settings);
2174                 g_object_unref (settings);
2175                 modest_account_settings_dialog_switch_to_user_info (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2176                 modest_account_settings_dialog_check_allow_changes (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2177                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
2178                 
2179                 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (account_name), dialog);
2180                 
2181                 g_signal_connect (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide), 
2182                                   g_strdup (account_name));
2183                         
2184                 /* Show it and delete it when it closes: */
2185                 g_signal_connect_swapped (dialog, 
2186                                           "response", 
2187                                           G_CALLBACK (gtk_widget_destroy), 
2188                                           dialog);
2189                 gtk_widget_show (GTK_WIDGET (dialog));
2190
2191                 return dialog;
2192         }
2193         
2194 }