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