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