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