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