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