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