78519e3d347685562d168b4b0ade860e899f8bb6
[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         
901         g_return_val_if_fail (account_mgr, NULL);
902         g_return_val_if_fail (device, NULL);
903
904         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
905         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
906
907         priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
908         priv->device = g_object_ref (device);
909         
910         priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
911         if (!priv->session) {
912                 g_warning ("failed to get TnySessionCamel");
913                 return NULL;
914         }
915
916         /* Set the ui locker */ 
917         tny_session_camel_set_ui_locker (priv->session,  tny_gtk_lockable_new ());
918         
919         /* Connect signals */
920         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
921                                                          G_OBJECT(account_mgr), "account_inserted",
922                                                          G_CALLBACK (on_account_inserted), obj);
923         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
924                                                          G_OBJECT(account_mgr), "account_changed",
925                                                          G_CALLBACK (on_account_changed), obj);
926         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
927                                                          G_OBJECT(account_mgr), "account_removed",
928                                                          G_CALLBACK (on_account_removed), obj);
929
930         /* Create the lists of accounts */
931         priv->store_accounts = tny_simple_list_new ();
932         priv->transport_accounts = tny_simple_list_new ();
933         priv->store_accounts_outboxes = tny_simple_list_new ();
934
935         /* Create the local folders account */
936         local_account = 
937                 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
938         tny_list_append (priv->store_accounts, G_OBJECT(local_account));
939         g_object_unref (local_account); 
940
941         /* Add the other remote accounts. Do this after adding the
942            local account, because we need to add our outboxes to the
943            global OUTBOX hosted in the local account */
944         add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
945         
946         /* Add connection-specific transport accounts if there are any
947            accounts available */
948         if (!only_local_accounts (MODEST_TNY_ACCOUNT_STORE(obj)))
949                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
950         
951         /* This is a singleton, so it does not need to be unrefed. */
952         if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
953                 /* It is mounted: */
954                 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */); 
955         }
956         
957         return MODEST_TNY_ACCOUNT_STORE(obj);
958 }
959
960 static void
961 modest_tny_account_store_get_accounts  (TnyAccountStore *self, 
962                                         TnyList *list,
963                                         TnyGetAccountsRequestType request_type)
964 {
965         ModestTnyAccountStorePrivate *priv;
966         
967         g_return_if_fail (self);
968         g_return_if_fail (TNY_IS_LIST(list));
969         
970         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
971         
972         switch (request_type) {
973         case TNY_ACCOUNT_STORE_BOTH:
974                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
975                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
976                 break;
977         case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
978                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
979                 break;
980         case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
981                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
982                 break;
983         default:
984                 g_return_if_reached ();
985         }
986
987         /* Initialize session. Why do we need this ??? */
988         tny_session_camel_set_initialized (priv->session);
989 }
990
991
992 static const gchar*
993 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
994 {
995         ModestTnyAccountStorePrivate *priv;
996         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
997         
998         if (!priv->cache_dir)
999                 priv->cache_dir = g_build_filename (g_get_home_dir(), 
1000                                                     MODEST_DIR, MODEST_CACHE_DIR, NULL);
1001         return priv->cache_dir;
1002 }
1003
1004
1005 /*
1006  * callers need to unref
1007  */
1008 static TnyDevice*
1009 modest_tny_account_store_get_device (TnyAccountStore *self)
1010 {
1011         ModestTnyAccountStorePrivate *priv;
1012
1013         g_return_val_if_fail (self, NULL);
1014         
1015         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1016         
1017         if (priv->device) 
1018                 return g_object_ref (G_OBJECT(priv->device));
1019         else
1020                 return NULL;
1021 }
1022
1023
1024 static TnyAccount*
1025 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1026 {
1027         return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self), 
1028                                                             MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1029                                                             url_string);
1030 }
1031
1032
1033
1034 static gboolean
1035 modest_tny_account_store_alert (TnyAccountStore *self, 
1036                                 TnyAccount *account, 
1037                                 TnyAlertType type,
1038                                 gboolean question, 
1039                                 GError *error)
1040 {
1041         ModestTransportStoreProtocol proto =
1042                 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN; 
1043         const gchar* server_name = "";
1044         gchar *prompt = NULL;
1045         gboolean retval;
1046
1047         /* NOTE: account may be NULL in some cases */
1048         g_return_val_if_fail (error, FALSE);
1049         
1050         /* Get the server name: */
1051         if (account) {
1052                 server_name = tny_account_get_hostname (account);
1053                 const gchar *proto_name = tny_account_get_proto (account);
1054                 if (proto_name)
1055                         proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1056                 else {
1057                         g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__, 
1058                                   tny_account_get_id (account));
1059                         return FALSE;
1060                 }
1061         }
1062
1063         switch (error->code) {
1064         case TNY_SYSTEM_ERROR_CANCEL:
1065                 /* Don't show waste the user's time by showing him a dialog telling 
1066                  * him that he has just cancelled something: */
1067                 return TRUE;
1068
1069         case TNY_SERVICE_ERROR_PROTOCOL:
1070                 /* Like a BAD from IMAP (protocol error) */
1071         case TNY_SERVICE_ERROR_LOST_CONNECTION:
1072                 /* Lost the connection with the service */
1073         case TNY_SERVICE_ERROR_UNAVAILABLE:
1074                 /* You must be working online for this operation */
1075         case TNY_SERVICE_ERROR_CONNECT:
1076                 switch (proto) {
1077                 case MODEST_PROTOCOL_STORE_POP:
1078                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1079                                                   server_name);
1080                         break;
1081                 case MODEST_PROTOCOL_STORE_IMAP:
1082                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1083                                                   server_name);
1084                         break;
1085                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1086                         prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1087                                                   server_name);
1088                         break;
1089                 default:
1090                         g_return_val_if_reached (FALSE);
1091                 }
1092                 break;
1093                 
1094         case TNY_SERVICE_ERROR_AUTHENTICATE:
1095                 /* It seems that there's no better error to show with
1096                  * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1097                  * may appear if there's a timeout during auth */
1098                 switch (proto) {
1099                 case MODEST_PROTOCOL_STORE_POP:
1100                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1101                                                   server_name);
1102                         break;
1103                 case MODEST_PROTOCOL_STORE_IMAP:
1104                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1105                                                   server_name);
1106                         break;
1107                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1108                         prompt = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
1109                                                   server_name);
1110                         break;
1111                 default:
1112                         g_return_val_if_reached (FALSE);
1113                 }
1114                 break;
1115                         
1116         case TNY_SERVICE_ERROR_CERTIFICATE:
1117                 /* We'll show the proper dialog later */
1118                 break;
1119
1120         case TNY_SYSTEM_ERROR_MEMORY:
1121                 /* Can't allocate memory for this operation */
1122
1123         case TNY_SERVICE_ERROR_UNKNOWN: 
1124                 return FALSE;                   
1125         default:
1126                 g_debug ("Unexpected error %d", error->code);
1127                 g_return_val_if_reached (FALSE);
1128         }
1129         
1130
1131         if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1132                 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1133                                                                               error->message);
1134         else if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE) {
1135                 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1136
1137                 /* Show the account dialog if it was wrong */
1138                 if (error->code == TNY_SERVICE_ERROR_CONNECT || 
1139                     error->code == TNY_SERVICE_ERROR_AUTHENTICATE)
1140                         show_wrong_password_dialog (account);
1141
1142                 retval = TRUE;
1143         }
1144
1145         g_debug ("%s: error code %d (%s", __FUNCTION__, error->code, error->message);
1146         
1147         if (prompt)
1148                 g_free (prompt);
1149         
1150         return retval;
1151 }
1152
1153
1154 static void
1155 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1156 {
1157         TnyAccountStoreIface *klass;
1158
1159         g_return_if_fail (g);
1160
1161         klass = (TnyAccountStoreIface *)g;
1162
1163         klass->get_accounts =
1164                 modest_tny_account_store_get_accounts;
1165         klass->get_cache_dir =
1166                 modest_tny_account_store_get_cache_dir;
1167         klass->get_device =
1168                 modest_tny_account_store_get_device;
1169         klass->alert =
1170                 modest_tny_account_store_alert;
1171         klass->find_account =
1172                 modest_tny_account_store_find_account_by_url;
1173 }
1174
1175 void
1176 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1177                                             ModestTnyGetPassFunc func)
1178 {
1179         /* not implemented, we use signals */
1180         g_printerr ("modest: set_get_pass_func not implemented\n");
1181 }
1182
1183 TnySessionCamel*
1184 modest_tny_account_store_get_session  (TnyAccountStore *self)
1185 {
1186         g_return_val_if_fail (self, NULL);
1187         return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1188 }
1189
1190 static TnyAccount*
1191 get_tny_account_by (TnyList *accounts,
1192                     ModestTnyAccountStoreQueryType type,
1193                     const gchar *str)
1194 {
1195         TnyIterator *iter = NULL;
1196         gboolean found = FALSE;
1197         TnyAccount *retval = NULL;
1198
1199         g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1200
1201         if (tny_list_get_length(accounts) == 0) {
1202                 g_warning ("%s: account list is empty", __FUNCTION__);
1203                 return NULL;
1204         }
1205         
1206         iter = tny_list_create_iterator (accounts);
1207         while (!tny_iterator_is_done (iter) && !found) {
1208                 TnyAccount *tmp_account = NULL;
1209                 const gchar *val = NULL;
1210
1211                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1212                 if (!TNY_IS_ACCOUNT(tmp_account)) {
1213                         g_warning ("%s: not a valid account", __FUNCTION__);
1214                         tmp_account = NULL;
1215                         break;
1216                 }
1217
1218                 switch (type) {
1219                 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1220                         val = tny_account_get_id (tmp_account);
1221                         break;
1222                 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1223                         val = tny_account_get_url_string (tmp_account);
1224                         break;
1225                 }
1226                 
1227                 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL && 
1228                     tny_account_matches_url_string (tmp_account, str)) {
1229                         retval = g_object_ref (tmp_account);
1230                         found = TRUE;
1231                 } else {
1232                         if (val && str && strcmp (val, str) == 0) {
1233                                 retval = g_object_ref (tmp_account);
1234                                 found = TRUE;
1235                         }
1236                 }
1237                 g_object_unref (tmp_account);
1238                 tny_iterator_next (iter);
1239         }
1240         g_object_unref (iter);
1241
1242         return retval;
1243 }
1244
1245 TnyAccount*
1246 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self, 
1247                                              ModestTnyAccountStoreQueryType type,
1248                                              const gchar *str)
1249 {
1250         TnyAccount *account = NULL;
1251         ModestTnyAccountStorePrivate *priv;     
1252         
1253         g_return_val_if_fail (self, NULL);
1254         g_return_val_if_fail (str, NULL);
1255         
1256         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1257         
1258         /* Search in store accounts */
1259         account = get_tny_account_by (priv->store_accounts, type, str);
1260
1261         /* If we already found something, no need to search the transport accounts */
1262         if (!account) {
1263                 account = get_tny_account_by (priv->transport_accounts, type, str);
1264
1265                 /* If we already found something, no need to search the
1266                    per-account outbox accounts */
1267                 if (!account)
1268                         account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1269         }
1270
1271         /* Warn if nothing was found. This is generally unusual. */
1272         if (!account) {
1273                 g_warning("%s: Failed to find account with %s=%s\n", 
1274                           __FUNCTION__, 
1275                           (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",                     
1276                           str);
1277         }
1278
1279         /* Returns a new reference to the account if found */   
1280         return account;
1281 }
1282
1283
1284 TnyAccount*
1285 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1286                                              const gchar *account_name,
1287                                              TnyAccountType type)
1288 {
1289         ModestTnyAccountStorePrivate *priv = NULL;
1290         TnyAccount *retval = NULL;
1291         TnyList *account_list = NULL;
1292         TnyIterator *iter = NULL;
1293         gboolean found;
1294
1295         g_return_val_if_fail (self, NULL);
1296         g_return_val_if_fail (account_name, NULL);
1297         g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || 
1298                               type == TNY_ACCOUNT_TYPE_TRANSPORT,
1299                               NULL);
1300         
1301         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1302
1303         account_list = (type == TNY_ACCOUNT_TYPE_STORE) ? 
1304                 priv->store_accounts : 
1305                 priv->transport_accounts;
1306
1307         if (!account_list) {
1308                 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__, 
1309                         (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1310                 return NULL;
1311         }
1312         
1313         /* Look for the server account */
1314         found = FALSE;
1315         iter = tny_list_create_iterator (account_list);
1316         while (!tny_iterator_is_done (iter) && !found) {
1317                 const gchar *modest_acc_name;
1318                 TnyAccount *tmp_account;
1319
1320                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1321                 modest_acc_name = 
1322                         modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1323                 
1324                 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1325                         found = TRUE;
1326                         retval = g_object_ref (tmp_account);
1327                 }
1328                 /* Free and continue */
1329                 g_object_unref (tmp_account);
1330                 tny_iterator_next (iter);
1331         }
1332         g_object_unref (iter);
1333
1334         if (!found) {
1335                 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1336                             "Number of server accounts of this type=%d\n", __FUNCTION__,
1337                             (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1338                             account_name, tny_list_get_length (account_list));
1339         }
1340
1341         /* Returns a new reference */
1342         return retval;
1343 }
1344
1345 TnyAccount*
1346 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1347         ModestTnyAccountStore *self, const gchar *account_name)
1348 {
1349         TnyDevice *device;
1350
1351         g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1352         g_return_val_if_fail (account_name, NULL);
1353
1354         /* Get the current connection: */
1355         device = modest_runtime_get_device ();
1356
1357         if (!device) {
1358                 g_warning ("%s: could not get device", __FUNCTION__);
1359                 return NULL;
1360         }
1361                 
1362         if (!tny_device_is_online (device))
1363                 return NULL;
1364         
1365 #ifdef MODEST_HAVE_CONIC
1366         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1367         
1368         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1369         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1370         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1371         if (!iap_id)
1372                 return NULL;
1373                 
1374         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1375         if (!connection)
1376                 return NULL;
1377                 
1378         const gchar *connection_id = con_ic_iap_get_id (connection);
1379         /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1380         if (!connection_id)
1381                 return NULL;
1382         
1383         /*  Get the connection-specific transport acccount, if any: */
1384         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1385
1386         /* Check if this account has connection-specific SMTP enabled */
1387         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1388                 return NULL;
1389         }
1390
1391         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1392                 connection_id);
1393
1394         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1395         if (!server_account_name) {
1396                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1397         }
1398                 
1399         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1400                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1401                                                                            server_account_name);
1402
1403         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1404         g_free (server_account_name);   
1405
1406         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1407         g_object_unref (connection);
1408         
1409         return account;
1410 #else
1411         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1412 #endif /* MODEST_HAVE_CONIC */
1413 }
1414
1415                                                                  
1416 TnyAccount*
1417 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1418                                                                     const gchar *account_name)
1419 {
1420         g_return_val_if_fail (self, NULL);
1421         g_return_val_if_fail (account_name, NULL);
1422
1423         if (!account_name || !self)
1424                 return NULL;
1425         
1426         /*  Get the connection-specific transport acccount, if any: */
1427         /* Note: This gives us a reference: */
1428         TnyAccount *account =
1429                 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1430                         
1431         /* If there is no connection-specific transport account (the common case), 
1432          * just get the regular transport account: */
1433         if (!account) {
1434                 /* The special local folders don't have transport accounts. */
1435                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1436                         account = NULL;
1437                 else {
1438                         /* Note: This gives us a reference: */
1439                         account = modest_tny_account_store_get_server_account (self, account_name, 
1440                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1441                 }
1442         }
1443                         
1444         /* returns a reference. */     
1445         return account;
1446 }
1447
1448 TnyAccount*
1449 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1450 {
1451         TnyAccount *account = NULL;
1452         ModestTnyAccountStorePrivate *priv;
1453         TnyIterator *iter;
1454         gboolean found;
1455
1456         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1457         
1458         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1459
1460         found = FALSE;
1461         iter = tny_list_create_iterator (priv->store_accounts);
1462         while (!tny_iterator_is_done (iter) && !found) {
1463                 TnyAccount *tmp_account;
1464
1465                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1466                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1467                         account = g_object_ref (tmp_account);
1468                         found = TRUE;
1469                 }
1470                 g_object_unref (tmp_account);
1471                 tny_iterator_next (iter);
1472         }
1473         g_object_unref (iter);
1474
1475         /* Returns a new reference to the account */
1476         return account;
1477 }
1478
1479 TnyAccount*
1480 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1481 {
1482         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1483         
1484         /* New reference */
1485         return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1486                                                             MODEST_MMC_ACCOUNT_ID);
1487
1488 }
1489
1490 /*********************************************************************************/
1491 static void
1492 add_existing_accounts (ModestTnyAccountStore *self)
1493 {
1494         GSList *account_names = NULL, *iter = NULL;
1495         ModestTnyAccountStorePrivate *priv = NULL;
1496         
1497         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1498
1499         /* These are account names, not server_account names */
1500         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1501
1502         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1503                 const gchar *account_name = (const gchar*) iter->data;
1504                 
1505                 /* Insert all enabled accounts without notifying */
1506                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1507                         insert_account (self, account_name, FALSE);
1508         }
1509         modest_account_mgr_free_account_names (account_names);
1510 }
1511
1512 static void 
1513 connection_status_changed (TnyAccount *account, 
1514                            TnyConnectionStatus status, 
1515                            gpointer data)
1516 {
1517         /* We do this here and not in the connection policy because we
1518            don't want to do it for every account, just for the
1519            accounts that are interactively added when modest is
1520            runnning */
1521         if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1522                 const gchar *account_name;
1523                 ModestWindow *main_window;
1524                 ModestTnyAccountStorePrivate *priv = NULL;
1525                 
1526                 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1527
1528                 /* Remove this handler */
1529                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, 
1530                                                                   G_OBJECT (account),
1531                                                                   "connection_status_changed");
1532
1533                 /* Perform a send receive */
1534                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1535                 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1536                 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, TRUE, main_window);
1537         }
1538 }
1539
1540 static TnyAccount*
1541 create_tny_account (ModestTnyAccountStore *self,
1542                     const gchar *name,
1543                     TnyAccountType type,
1544                     gboolean notify)
1545 {
1546         TnyAccount *account = NULL;
1547         ModestTnyAccountStorePrivate *priv = NULL;
1548         
1549         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1550
1551         account = modest_tny_account_new_from_account (priv->account_mgr,
1552                                                        name, type, 
1553                                                        priv->session,
1554                                                        get_password,
1555                                                        forget_password);
1556
1557         if (account) {
1558                 /* Forget any cached password for the account, so that
1559                    we use a new account if any */
1560                 forget_password_in_memory (self, tny_account_get_id (account));
1561
1562                 /* Install a signal handler that will refresh the
1563                    account the first time it becomes online. Do this
1564                    only if we're adding a new account while the
1565                    program is running (we do not want to do this
1566                    allways) */
1567                 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1568                         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, 
1569                                                                        G_OBJECT (account), 
1570                                                                        "connection_status_changed",
1571                                                                        G_CALLBACK (connection_status_changed),
1572                                                                        self);
1573
1574                 /* Set the account store */
1575                 g_object_set_data (G_OBJECT(account), "account_store", self);
1576         } else {
1577                 g_printerr ("modest: failed to create account for %s\n", name);
1578         }
1579
1580         return account;
1581 }
1582
1583 typedef struct _AddOutboxInfo {
1584         ModestTnyAccountStore *account_store;
1585         TnyAccount *transport_account;
1586 } AddOutboxInfo;
1587
1588 static void
1589 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1590                                                                    gboolean cancelled,
1591                                                                    TnyList *list,
1592                                                                    GError *err,
1593                                                                    gpointer userdata)
1594 {
1595         TnyIterator *iter_folders;
1596         TnyFolder *per_account_outbox;
1597         TnyAccount *local_account = NULL;
1598         AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1599         ModestTnyAccountStorePrivate *priv = NULL;
1600         ModestTnyAccountStore *self;
1601
1602         self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1603         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1604         
1605         /* Note that this could happen if there is not enough space
1606            available on disk, then the outbox folder could not be
1607            created */
1608         if (tny_list_get_length (list) != 1) {
1609                 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1610                            tny_list_get_length (list));
1611                 goto frees;
1612         }
1613                         
1614         iter_folders = tny_list_create_iterator (list);
1615         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1616         g_object_unref (iter_folders);
1617         g_object_unref (list);
1618
1619         /* Add the outbox of the new per-account-local-outbox account
1620            to the global local merged OUTBOX of the local folders
1621            account */
1622         local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1623         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1624                                                                per_account_outbox);
1625         /* Add the pair to the hash table */
1626         g_hash_table_insert (priv->outbox_of_transport,
1627                              info->transport_account,
1628                              per_account_outbox);
1629         
1630         /* Notify that the local account changed */
1631         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1632         g_object_unref (local_account);
1633         g_object_unref (per_account_outbox);
1634
1635  frees:
1636         g_object_unref (info->transport_account);
1637         g_slice_free (AddOutboxInfo, info);
1638 }
1639                                                                    
1640
1641 static void
1642 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1643                                                     const gchar *account_name,
1644                                                     TnyAccount *transport_account)
1645 {
1646         TnyList *folders = NULL;
1647         TnyAccount *account_outbox = NULL;
1648         ModestTnyAccountStorePrivate *priv = NULL;
1649         AddOutboxInfo *info;
1650
1651         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1652
1653         /* Create per account local outbox */
1654         account_outbox = 
1655                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1656                                                                             account_name, 
1657                                                                             priv->session);
1658
1659         if (!G_IS_OBJECT (account_outbox)) {
1660                 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1661                 return;
1662         }
1663
1664         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1665         
1666         /* Get the outbox folder */
1667         folders = tny_simple_list_new ();
1668         info = g_slice_new0 (AddOutboxInfo);
1669         info->account_store = self;
1670         info->transport_account = g_object_ref (transport_account);
1671         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL, 
1672                                             add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1673         g_object_unref (account_outbox);
1674 }
1675
1676 /*
1677  * This function will be used for both adding new accounts and for the
1678  * initialization. In the initialization we do not want to emit
1679  * signals so notify will be FALSE, in the case of account additions
1680  * we do want to notify the observers
1681  */
1682 static void
1683 insert_account (ModestTnyAccountStore *self,
1684                 const gchar *account,
1685                 gboolean notify)
1686 {
1687         ModestTnyAccountStorePrivate *priv = NULL;
1688         TnyAccount *store_account = NULL, *transport_account = NULL;
1689         
1690         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1691
1692         /* Get the server and the transport account */
1693         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1694         if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1695                 g_warning ("%s: failed to create store account", __FUNCTION__);
1696                 return;
1697         }
1698
1699         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1700         if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1701                 g_warning ("%s: failed to create transport account", __FUNCTION__);
1702                 g_object_unref (store_account);
1703                 return;
1704         }
1705
1706         /* Add accounts to the lists */
1707         tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1708         tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1709         
1710         /* Create a new pseudo-account with an outbox for this
1711            transport account and add it to the global outbox
1712            in the local account */
1713         add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);  
1714
1715         /* Notify the observers. We do it after everything is
1716            created */
1717         if (notify) {
1718                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);   
1719                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1720         }
1721
1722         /* Frees */
1723         g_object_unref (store_account);
1724         g_object_unref (transport_account);
1725 }
1726
1727 static gboolean
1728 only_local_accounts (ModestTnyAccountStore *self)
1729 {
1730         ModestTnyAccountStorePrivate *priv = NULL;
1731         gboolean only_local = TRUE;
1732         TnyIterator *iter;
1733
1734         /* Check if this is the first remote account we add */
1735         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1736         iter = tny_list_create_iterator (priv->store_accounts);
1737
1738         while (!tny_iterator_is_done (iter) && only_local) {
1739                 TnyAccount *account = (TnyAccount *) tny_iterator_get_current (iter);
1740                 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1741                         only_local = FALSE;
1742                 g_object_unref (account);
1743                 tny_iterator_next (iter);
1744         }
1745         g_object_unref (iter);
1746
1747         return only_local;
1748 }
1749
1750 static void
1751 on_account_inserted (ModestAccountMgr *acc_mgr, 
1752                      const gchar *account,
1753                      gpointer user_data)
1754 {
1755         gboolean add_specific;
1756
1757         add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1758                 
1759         /* Insert the account and notify the observers */
1760         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1761
1762         /* If it's the first remote account then add the connection
1763            specific SMTP servers as well */
1764         if (add_specific)
1765                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1766
1767 }
1768
1769 /* This is the callback of the tny_camel_account_set_online called in
1770    on_account_removed to disconnect the account */
1771 static void
1772 on_account_disconnect_when_removing (TnyCamelAccount *account, 
1773                                      gboolean canceled, 
1774                                      GError *err, 
1775                                      gpointer user_data)
1776 {
1777         ModestTnyAccountStore *self;
1778         ModestTnyAccountStorePrivate *priv;
1779
1780         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1781         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1782
1783         /* Remove the connection-status-changed handler if it's still there */
1784         if (modest_signal_mgr_is_connected (priv->sighandlers, 
1785                                             G_OBJECT (account),
1786                                             "connection_status_changed")) {
1787                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, 
1788                                                                   G_OBJECT (account),
1789                                                                   "connection_status_changed");
1790         }
1791
1792         /* Cancel all pending operations */
1793         tny_account_cancel (TNY_ACCOUNT (account));
1794         
1795         /* Unref the extra reference added by get_server_account */
1796         g_object_unref (account);
1797
1798         /* Clear the cache if it's an store account */
1799         if (TNY_IS_STORE_ACCOUNT (account)) {
1800                 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1801         } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1802                 ModestTnySendQueue* send_queue;
1803                 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1804                 if (TNY_IS_SEND_QUEUE (send_queue)) {
1805                         if (modest_tny_send_queue_sending_in_progress (send_queue))
1806                                 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1807                                                        TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE, 
1808                                                        NULL);
1809                         modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1810                 }
1811         }
1812 }
1813
1814 /*
1815  * We use this one for both removing "normal" and "connection
1816  * specific" transport accounts
1817  */
1818 static void
1819 remove_transport_account (ModestTnyAccountStore *self,
1820                           TnyTransportAccount *transport_account)
1821 {
1822         ModestTnyAccountStorePrivate *priv;
1823         TnyFolder *outbox = NULL;
1824         
1825         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1826
1827         /* Remove it from the list of accounts and notify the
1828            observers. Do not need to wait for account
1829            disconnection */
1830         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1831         tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1832                 
1833         /* Remove the OUTBOX of the account from the global outbox */
1834         outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1835
1836         if (TNY_IS_FOLDER (outbox)) {
1837                 TnyAccount *local_account = NULL;
1838                 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1839
1840                 if (outbox_account) {
1841                         tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1842                         /* Remove existing emails to send */
1843                         tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1844                         g_object_unref (outbox_account);
1845                 }
1846
1847                 local_account = modest_tny_account_store_get_local_folders_account (self);
1848                 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1849                                                                             outbox);
1850
1851                 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1852
1853                 /* Notify the change in the local account */
1854                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1855                 g_object_unref (local_account);
1856         } else {
1857                 g_warning ("Removing a transport account that has no outbox");
1858         }
1859
1860         /* Cancel all pending operations */
1861         tny_account_cancel (TNY_ACCOUNT (transport_account));
1862
1863         /* Disconnect and notify the observers. The callback will free the reference */
1864         tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1865                                       on_account_disconnect_when_removing, self);
1866 }
1867
1868 static gboolean
1869 images_cache_remove_filter (TnyStreamCache *self, const gchar *id, const gchar *account_name)
1870 {
1871         gchar *account_name_with_separator;
1872         gboolean result;
1873         if (account_name == NULL || account_name[0] == '\0')
1874                 return FALSE;
1875
1876         if (id == NULL || id[0] == '\0')
1877                 return FALSE;
1878
1879         account_name_with_separator = g_strconcat (account_name, "__", NULL);
1880
1881         result = (g_str_has_prefix (id, account_name));
1882         g_free (account_name_with_separator);
1883
1884         return result;
1885 }
1886
1887 static void
1888 on_account_removed (ModestAccountMgr *acc_mgr, 
1889                     const gchar *account,
1890                     gpointer user_data)
1891 {
1892         TnyAccount *store_account = NULL, *transport_account = NULL;
1893         ModestTnyAccountStore *self;
1894         ModestTnyAccountStorePrivate *priv;
1895         TnyStreamCache *stream_cache;
1896         
1897         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1898         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1899
1900         /* Get the server and the transport account */
1901         store_account = 
1902                 modest_tny_account_store_get_server_account (self, account, 
1903                                                              TNY_ACCOUNT_TYPE_STORE);
1904         transport_account = 
1905                 modest_tny_account_store_get_server_account (self, account,
1906                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1907         
1908         /* If there was any problem creating the account, for example,
1909            with the configuration system this could not exist */
1910         if (TNY_IS_STORE_ACCOUNT(store_account)) {
1911                 /* Forget any cached password for the account */
1912                 forget_password_in_memory (self, tny_account_get_id (store_account));
1913
1914                 /* Remove it from the list of accounts and notify the
1915                    observers. Do not need to wait for account
1916                    disconnection */
1917                 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1918                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1919
1920                 /* Cancel all pending operations */
1921                 tny_account_cancel (TNY_ACCOUNT (store_account));
1922
1923                 /* Disconnect before deleting the cache, because the
1924                    disconnection will rewrite the cache to the
1925                    disk */
1926                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1927                                               on_account_disconnect_when_removing, self);
1928         } else {
1929                 g_warning ("%s: no store account for account %s\n", 
1930                            __FUNCTION__, account);
1931         }
1932
1933         /* If there was any problem creating the account, for example,
1934            with the configuration system this could not exist */
1935         if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1936
1937                 /* Forget any cached password for the account */
1938                 forget_password_in_memory (self, tny_account_get_id (transport_account));
1939
1940                 /* Remove transport account. It'll free the reference
1941                    added by get_server_account */
1942                 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1943         } else {
1944                 g_warning ("%s: no transport account for account %s\n", 
1945                            __FUNCTION__, account);
1946         }
1947
1948         /* Remove cached images */
1949         stream_cache = modest_runtime_get_images_cache ();
1950         tny_stream_cache_remove (stream_cache, (TnyStreamCacheRemoveFilter) images_cache_remove_filter, (gpointer) account);
1951
1952         /* If there are no more user accounts then delete the
1953            transport specific SMTP servers */
1954         if (only_local_accounts (self))
1955                 remove_connection_specific_transport_accounts (self);
1956 }
1957
1958 TnyTransportAccount *
1959 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1960                                                                     const gchar *name)
1961 {
1962         ModestTnyAccountStorePrivate *priv = NULL;
1963         TnyAccount * tny_account = NULL;
1964
1965         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1966
1967         /* Add the account: */
1968         tny_account = 
1969                 modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1970                                                                  priv->session, 
1971                                                                  name,
1972                                                                  get_password,
1973                                                                  forget_password);
1974         if (tny_account) {
1975                 g_object_set_data (G_OBJECT(tny_account), 
1976                                    "account_store", 
1977                                    (gpointer)self);
1978                 g_object_set_data (G_OBJECT(tny_account), 
1979                                    "connection_specific", 
1980                                    GINT_TO_POINTER (TRUE));
1981                 
1982                 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1983                 add_outbox_from_transport_account_to_global_outbox (self, 
1984                                                                     name, 
1985                                                                     tny_account);
1986                 
1987         } else
1988                 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1989                             name);
1990
1991         return TNY_TRANSPORT_ACCOUNT (tny_account);
1992 }
1993
1994
1995 static void
1996 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1997 {
1998         ModestTnyAccountStorePrivate *priv = NULL;
1999         GSList *list_specifics = NULL, *iter = NULL;
2000         GError *err = NULL;
2001
2002         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2003
2004         list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2005                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2006                                                MODEST_CONF_VALUE_STRING, &err);
2007         if (err) {
2008                 g_error_free (err);
2009                 g_return_if_reached ();
2010         }
2011                                 
2012         /* Look at each connection-specific transport account for the 
2013          * modest account: */
2014         iter = list_specifics;
2015         while (iter) {
2016                 /* The list alternates between the connection name and the transport name: */
2017                 iter = g_slist_next (iter);
2018                 if (iter) {
2019                         const gchar* transport_account_name = (const gchar*) (iter->data);
2020                         TnyTransportAccount * account = NULL;
2021                         account = modest_tny_account_store_new_connection_specific_transport_account (
2022                                 self, transport_account_name);
2023                         if (account)
2024                                 g_object_unref (account);
2025                 }                               
2026                 iter = g_slist_next (iter);
2027         }
2028 }
2029
2030 static void
2031 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2032 {
2033         ModestTnyAccountStorePrivate *priv = NULL;
2034         GSList *list_specifics = NULL, *iter = NULL;
2035         GError *err = NULL;
2036
2037         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2038
2039         err = NULL;
2040         list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2041                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2042                                                MODEST_CONF_VALUE_STRING, &err);
2043         if (err) {
2044                 g_error_free (err);
2045                 g_return_if_reached ();
2046         }
2047                                 
2048         /* Look at each connection-specific transport account for the 
2049          * modest account: */
2050         iter = list_specifics;
2051         while (iter) {
2052                 /* The list alternates between the connection name and the transport name: */
2053                 iter = g_slist_next (iter);
2054                 if (iter) {
2055                         const gchar* transport_account_name = (const gchar*) (iter->data);
2056                         TnyAccount * account;
2057                         account = modest_tny_account_store_get_server_account (self,
2058                                                                                transport_account_name,
2059                                                                                TNY_ACCOUNT_TYPE_TRANSPORT);
2060
2061                         /* the call will free the reference */
2062                         if (account)
2063                                 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2064                 }                               
2065                 iter = g_slist_next (iter);
2066         }
2067 }
2068
2069
2070 TnyMsg *
2071 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self, 
2072                                                const gchar *uri,
2073                                                TnyAccount **ac_out)
2074 {
2075         TnyIterator *acc_iter;
2076         ModestTnyAccountStorePrivate *priv;
2077         TnyMsg *msg = NULL;
2078         TnyAccount *msg_account = NULL;
2079
2080         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2081         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2082
2083         acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2084         while (!msg && !tny_iterator_is_done (acc_iter)) {
2085                 TnyList *folders = tny_simple_list_new ();
2086                 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2087                 TnyIterator *folders_iter = NULL;
2088
2089                 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
2090                 folders_iter = tny_list_create_iterator (folders);
2091
2092                 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2093                         TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2094                         msg = tny_folder_find_msg (folder, uri, NULL);
2095
2096                         if (msg)
2097                                 msg_account = g_object_ref (account);
2098
2099                         g_object_unref (folder);
2100                         tny_iterator_next (folders_iter);
2101                 }
2102                 g_object_unref (folders_iter);
2103
2104                 g_object_unref (folders);
2105                 g_object_unref (account);
2106                 tny_iterator_next (acc_iter);
2107         }
2108
2109         g_object_unref (acc_iter);
2110
2111         if (ac_out != NULL)
2112                 *ac_out = msg_account;
2113
2114         return msg;
2115 }
2116
2117 TnyTransportAccount *
2118 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2119 {
2120         TnyIterator *acc_iter;
2121         ModestTnyAccountStorePrivate *priv;
2122         TnyTransportAccount *header_acc = NULL;
2123         gchar *msg_id;
2124
2125         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2126         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2127         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2128         msg_id = modest_tny_send_queue_get_msg_id (header);
2129         acc_iter = tny_list_create_iterator (priv->transport_accounts);
2130         while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2131                 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2132                 ModestTnySendQueue *send_queue;
2133                 ModestTnySendQueueStatus status;
2134                 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2135                 if (TNY_IS_SEND_QUEUE (send_queue)) {
2136                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2137                         if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2138                                 header_acc = g_object_ref(account);
2139                 }
2140                 g_object_unref (account);
2141                 tny_iterator_next (acc_iter);
2142         }
2143         g_object_unref(acc_iter);
2144         g_free (msg_id);
2145
2146         /* New reference */
2147         return header_acc;
2148 }
2149
2150 GtkWidget *
2151 modest_tny_account_store_show_account_settings_dialog (ModestTnyAccountStore *self,
2152                                                        const gchar *account_name)
2153 {
2154         ModestTnyAccountStorePrivate *priv;
2155         gpointer dialog_as_gpointer = NULL;
2156         gboolean found;
2157
2158         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2159         found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
2160                                               account_name, NULL, (gpointer*)&dialog_as_gpointer);
2161
2162         if (found) {
2163                 modest_account_settings_dialog_check_allow_changes ((ModestAccountSettingsDialog *) dialog_as_gpointer);
2164                 return (GtkWidget *) dialog_as_gpointer;
2165         } else {
2166                 ModestAccountSettings *settings;
2167                 GtkWidget *dialog;
2168                 dialog = (GtkWidget *) modest_account_settings_dialog_new ();
2169                 settings = modest_account_mgr_load_account_settings (priv->account_mgr, account_name);
2170                 modest_account_settings_dialog_set_account (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog), settings);
2171                 g_object_unref (settings);
2172                 modest_account_settings_dialog_switch_to_user_info (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2173                 modest_account_settings_dialog_check_allow_changes (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
2174                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
2175                 
2176                 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (account_name), dialog);
2177                 
2178                 g_signal_connect (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide), 
2179                                   g_strdup (account_name));
2180                         
2181                 /* Show it and delete it when it closes: */
2182                 g_signal_connect_swapped (dialog, 
2183                                           "response", 
2184                                           G_CALLBACK (gtk_widget_destroy), 
2185                                           dialog);
2186                 gtk_widget_show (GTK_WIDGET (dialog));
2187
2188                 return dialog;
2189         }
2190         
2191 }
2192
2193 typedef struct {
2194         ModestTnyAccountStore *account_store;
2195         ModestTnyAccountStoreShutdownCallback callback;
2196         gpointer userdata;
2197         gint pending;
2198 } ShutdownOpData;
2199
2200 static void
2201 account_shutdown_callback (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer userdata)
2202 {
2203         ShutdownOpData *op_data = (ShutdownOpData *) userdata;
2204         op_data->pending--;
2205         if (op_data->pending == 0) {
2206                 if (op_data->callback)
2207                         op_data->callback (op_data->account_store, op_data->userdata);
2208                 g_object_unref (op_data->account_store);
2209                 g_free (op_data);
2210         }
2211 }
2212
2213 static void
2214 account_shutdown (TnyAccount *account, ShutdownOpData *op_data)
2215 {
2216         g_return_if_fail (account && TNY_IS_ACCOUNT(account));
2217
2218         if (TNY_IS_STORE_ACCOUNT (account) && 
2219             !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
2220                 return;
2221
2222         op_data->pending++;
2223
2224         tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, account_shutdown_callback, op_data);
2225 }
2226
2227
2228 void 
2229 modest_tny_account_store_shutdown (ModestTnyAccountStore *self,
2230                                    ModestTnyAccountStoreShutdownCallback callback,
2231                                    gpointer userdata)
2232 {
2233         ShutdownOpData *op_data = g_new0 (ShutdownOpData, 1);
2234         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2235         op_data->callback = callback;
2236         op_data->userdata = userdata;
2237         op_data->pending = 0;
2238         op_data->account_store = g_object_ref (self);
2239         
2240         /* Destroy all accounts. Disconnect all accounts before they are destroyed */
2241         if (priv->store_accounts) {
2242                 tny_list_foreach (priv->store_accounts, (GFunc)account_shutdown, op_data);
2243         }
2244         
2245         if (priv->transport_accounts) {
2246                 tny_list_foreach (priv->transport_accounts, (GFunc)account_shutdown, op_data);
2247         }
2248
2249         if (op_data->pending == 0) {
2250                 if (op_data->callback)
2251                         op_data->callback (op_data->account_store, op_data->userdata);
2252                 g_object_unref (op_data->account_store);
2253                 g_free (op_data);
2254         }
2255 }