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