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