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