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