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