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