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