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