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