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