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