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