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