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