* Fixes NB#82632, show the proper error note when the username is dimmed in the...
[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    modest_tny_account_store_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  * modest_tny_account_store_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 modest_tny_account_store_forget_password_in_memory (ModestTnyAccountStore *self, const gchar * server_account_name)
446 {
447         /* printf ("DEBUG: %s\n", __FUNCTION__); */
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         const TnyAccountStore *account_store = NULL;
551         ModestTnyAccountStore *self = NULL;
552         ModestTnyAccountStorePrivate *priv;
553         gchar *username = NULL;
554         gchar *pwd = NULL;
555         gpointer pwd_ptr = NULL;
556         gboolean already_asked = FALSE;
557
558         g_return_val_if_fail (account, NULL);
559         
560         MODEST_DEBUG_BLOCK(
561                 g_debug ("%s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
562         );
563         
564         /* Initialize the output parameter: */
565         if (cancel)
566                 *cancel = FALSE;
567                 
568         const gchar *server_account_name = tny_account_get_id (account);
569         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
570                                                              "account_store"));
571
572         if (!server_account_name || !account_store) {
573                 g_warning ("modest: %s: could not retrieve account_store for account %s",
574                            __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
575                 if (cancel)
576                         *cancel = TRUE;
577                 
578                 return NULL;
579         }
580
581         self = MODEST_TNY_ACCOUNT_STORE (account_store);
582         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
583         
584         /* This hash map stores passwords, including passwords that are not stored in gconf. */
585         /* Is it in the hash? if it's already there, it must be wrong... */
586         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
587                                    * type-punned ptrs...*/
588         already_asked = priv->password_hash && 
589                                 g_hash_table_lookup_extended (priv->password_hash,
590                                                       server_account_name,
591                                                       NULL,
592                                                       (gpointer*)&pwd_ptr);
593         MODEST_DEBUG_BLOCK(
594                 g_debug ("%s: Already asked = %d\n", __FUNCTION__, already_asked);
595         );
596                 
597         /* If the password is not already there, try ModestConf */
598         if (!already_asked) {
599                 pwd  = modest_account_mgr_get_server_account_password (priv->account_mgr,
600                                                                        server_account_name);
601                 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
602         }
603
604         /* If it was already asked, it must have been wrong, so ask again */
605         if (already_asked || !pwd || strlen(pwd) == 0) {
606                 /* As per the UI spec, if no password was set in the account settings, 
607                  * ask for it now. But if the password is wrong in the account settings, 
608                  * then show a banner and the account settings dialog so it can be corrected:
609                  */
610                 ModestTransportStoreProtocol proto;
611                 const gboolean settings_have_password = 
612                         modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
613
614                 /* Show an error and after that ask for a password */
615                 proto = modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (account));
616                 if (proto == MODEST_PROTOCOL_TRANSPORT_SMTP) {
617                         gchar *username = NULL, *msg = NULL;
618                         username = modest_account_mgr_get_server_account_username (priv->account_mgr,
619                                                                                    server_account_name);
620                         if (!username || strlen(username) == 0) {
621                                 msg = g_strdup_printf (_("emev_ni_ui_smtp_userid_invalid"), 
622                                                        tny_account_get_name (account),
623                                                        tny_account_get_hostname (account));
624                         } else {
625                                 gchar *password;
626                                 password  = modest_account_mgr_get_server_account_password (priv->account_mgr,
627                                                                                             server_account_name);
628                                 if (!password || strlen(password) == 0)
629                                         msg = g_strdup_printf (_("emev_ni_ui_smtp_passwd_invalid"), 
630                                                                tny_account_get_name (account),
631                                                                tny_account_get_hostname (account));
632                                 else
633                                         msg = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"), 
634                                                                tny_account_get_hostname (account));
635                                 if (password)
636                                         g_free (password);
637                         }
638                         if (msg) {
639                                 modest_platform_run_information_dialog (NULL, msg, TRUE);
640                                 g_free (msg);
641                         }
642                         if (username)
643                                 g_free (username);
644                 }
645
646                 if (settings_have_password) {
647                         /* The password must be wrong, so show the account settings dialog so it can be corrected: */
648                         show_wrong_password_dialog (account);
649                         
650                         if (cancel)
651                                 *cancel = TRUE;
652                                 
653                         return NULL;
654                 }
655         
656                 /* we don't have it yet. Get the password from the user */
657                 const gchar* account_id = tny_account_get_id (account);
658                 gboolean remember = FALSE;
659                 pwd = NULL;
660                 
661                 if (already_asked) {
662                         const gchar *msg;
663                         gboolean username_known = 
664                                 modest_account_mgr_get_server_account_username_has_succeeded(priv->account_mgr, 
665                                                                                              server_account_name);
666                         /* If the login has ever succeeded then show a specific message */
667                         if (username_known)
668                                 msg = _("ecdg_ib_set_password_incorrect");
669                         else
670                                 msg = _("mcen_ib_username_pw_incorrect");
671                         show_password_warning_only (msg);
672                 }
673
674                 /* Request password */
675                 g_signal_emit (G_OBJECT(account_store), signals[PASSWORD_REQUESTED_SIGNAL], 0,
676                                account_id, /* server_account_name */
677                                &username, &pwd, cancel, &remember);
678
679                 
680                 if (!*cancel) {
681                         /* The password will be returned as the result,
682                          * but we need to tell tinymail about the username too: */
683                         tny_account_set_user (account, username);
684                         
685                         /* Do not save the password in gconf, because
686                          * the UI spec says "The password will never
687                          * be saved in the account": */
688
689                         /* We need to dup the string even knowing that
690                            it's already a dup of the contents of an
691                            entry, because it if it's wrong, then camel
692                            will free it */
693                         g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
694                 } else {
695                         g_hash_table_remove (priv->password_hash, server_account_name);
696                         
697                         g_free (pwd);
698                         pwd = NULL;
699                 }
700
701                 g_free (username);
702                 username = NULL;
703         } else
704                 if (cancel)
705                         *cancel = FALSE;        
706         return pwd;
707 }
708
709 void 
710 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
711 {
712         g_return_if_fail (account);
713           
714         ModestTnyAccountStorePrivate *priv;
715         gchar *pwd = NULL;
716         gpointer pwd_ptr = NULL;
717         gboolean already_asked = FALSE;
718                 
719         const gchar *server_account_name = tny_account_get_id (account);
720
721         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
722         
723         /* This hash map stores passwords, including passwords that are not stored in gconf. */
724         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
725                                    * type-punned ptrs...*/
726         already_asked = priv->password_hash && 
727                                 g_hash_table_lookup_extended (priv->password_hash,
728                                                       server_account_name,
729                                                       NULL,
730                                                       (gpointer*)&pwd_ptr);
731
732         if (already_asked) {
733                 g_hash_table_remove (priv->password_hash, server_account_name);         
734                 g_free (pwd);
735                 pwd = NULL;
736         }
737
738         return;
739 }
740
741 /* tinymail calls this if the connection failed due to an incorrect password.
742  * And it seems to call this for any general connection failure. */
743 static void
744 forget_password (TnyAccount *account)
745 {
746         ModestTnyAccountStore *self;
747         ModestTnyAccountStorePrivate *priv;
748         const TnyAccountStore *account_store;
749         gchar *pwd;
750         const gchar *key;
751         
752         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
753                                                              "account_store"));
754         self = MODEST_TNY_ACCOUNT_STORE (account_store);
755         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
756         key  = tny_account_get_id (account);
757
758         /* Do not remove the key, this will allow us to detect that we
759            have already asked for it at least once */
760         pwd = g_hash_table_lookup (priv->password_hash, key);
761         if (pwd) {
762                 memset (pwd, 0, strlen (pwd));
763                 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
764         }
765
766         /* Remove from configuration system */
767         /*
768         modest_account_mgr_unset (priv->account_mgr,
769                                   key, MODEST_ACCOUNT_PASSWORD, TRUE);
770         */
771 }
772
773 static void
774 modest_tny_account_store_finalize (GObject *obj)
775 {
776         ModestTnyAccountStore *self        = MODEST_TNY_ACCOUNT_STORE(obj);
777         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
778
779         g_free (priv->cache_dir);
780         priv->cache_dir = NULL;
781         
782         if (priv->password_hash) {
783                 g_hash_table_destroy (priv->password_hash);
784                 priv->password_hash = NULL;
785         }
786
787         if (priv->account_settings_dialog_hash) {
788                 g_hash_table_destroy (priv->account_settings_dialog_hash);
789                 priv->account_settings_dialog_hash = NULL;
790         }
791
792         if (priv->outbox_of_transport) {
793                 g_hash_table_destroy (priv->outbox_of_transport);
794                 priv->outbox_of_transport = NULL;
795         }
796
797         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
798         priv->sighandlers = NULL;       
799
800         if (priv->account_mgr) {
801                 g_object_unref (G_OBJECT(priv->account_mgr));
802                 priv->account_mgr = NULL;
803         }
804
805         if (priv->device) {
806                 g_object_unref (G_OBJECT(priv->device));
807                 priv->device = NULL;
808         }
809
810         /* Destroy all accounts. Disconnect all accounts before they are destroyed */
811         if (priv->store_accounts) {
812                 tny_list_foreach (priv->store_accounts, (GFunc)account_disconnect, NULL);
813                 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
814                 g_object_unref (priv->store_accounts);
815                 priv->store_accounts = NULL;
816         }
817         
818         if (priv->transport_accounts) {
819                 tny_list_foreach (priv->transport_accounts, (GFunc)account_disconnect, NULL);
820                 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
821                 g_object_unref (priv->transport_accounts);
822                 priv->transport_accounts = NULL;
823         }
824
825         if (priv->store_accounts_outboxes) {
826                 g_object_unref (priv->store_accounts_outboxes);
827                 priv->store_accounts_outboxes = NULL;
828         }
829                 
830         if (priv->session) {
831                 camel_object_unref (CAMEL_OBJECT(priv->session));
832                 priv->session = NULL;
833         }
834
835         camel_shutdown ();
836         
837         G_OBJECT_CLASS(parent_class)->finalize (obj);
838 }
839
840 gboolean 
841 volume_path_is_mounted (const gchar* path)
842 {
843         g_return_val_if_fail (path, FALSE);
844
845         gboolean result = FALSE;
846         gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
847         g_return_val_if_fail (path_as_uri, FALSE);
848
849         /* Get the monitor singleton: */
850         GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
851
852         /* This seems like a simpler way to do this, but it returns a   
853          * GnomeVFSVolume even if the drive is not mounted: */
854         /*
855         GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor, 
856                 MODEST_MCC1_VOLUMEPATH);
857         if (volume) {
858                 gnome_vfs_volume_unref(volume);
859         }
860         */
861
862         /* Get the mounted volumes from the monitor: */
863         GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
864         GList *iter = list;
865         for (iter = list; iter; iter = g_list_next (iter)) {
866                 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
867                 if (volume) {
868                         /*
869                         char *display_name = 
870                                 gnome_vfs_volume_get_display_name (volume);
871                         printf ("volume display name=%s\n", display_name);
872                         g_free (display_name);
873                         */
874                         
875                         char *uri = 
876                                 gnome_vfs_volume_get_activation_uri (volume);
877                         /* printf ("  uri=%s\n", uri); */
878                         if (uri && (strcmp (uri, path_as_uri) == 0))
879                                 result = TRUE;
880
881                         g_free (uri);
882
883                         gnome_vfs_volume_unref (volume);
884                 }
885         }
886
887         g_list_free (list);
888
889         g_free (path_as_uri);
890
891         return result;
892 }
893
894 ModestTnyAccountStore*
895 modest_tny_account_store_new (ModestAccountMgr *account_mgr, 
896                               TnyDevice *device) 
897 {
898         GObject *obj;
899         ModestTnyAccountStorePrivate *priv;
900         TnyAccount *local_account = NULL;
901         
902         g_return_val_if_fail (account_mgr, NULL);
903         g_return_val_if_fail (device, NULL);
904
905         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
906         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
907
908         priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
909         priv->device = g_object_ref (device);
910         
911         priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
912         if (!priv->session) {
913                 g_warning ("failed to get TnySessionCamel");
914                 return NULL;
915         }
916
917         /* Set the ui locker */ 
918         tny_session_camel_set_ui_locker (priv->session,  tny_gtk_lockable_new ());
919         
920         /* Connect signals */
921         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
922                                                          G_OBJECT(account_mgr), "account_inserted",
923                                                          G_CALLBACK (on_account_inserted), obj);
924         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
925                                                          G_OBJECT(account_mgr), "account_changed",
926                                                          G_CALLBACK (on_account_changed), obj);
927         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
928                                                          G_OBJECT(account_mgr), "account_removed",
929                                                          G_CALLBACK (on_account_removed), obj);
930
931         /* Create the lists of accounts */
932         priv->store_accounts = tny_simple_list_new ();
933         priv->transport_accounts = tny_simple_list_new ();
934         priv->store_accounts_outboxes = tny_simple_list_new ();
935
936         /* Create the local folders account */
937         local_account = 
938                 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
939         tny_list_append (priv->store_accounts, G_OBJECT(local_account));
940         g_object_unref (local_account); 
941
942         /* Add the other remote accounts. Do this after adding the
943            local account, because we need to add our outboxes to the
944            global OUTBOX hosted in the local account */
945         add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
946         
947         /* FIXME: I'm doing this (adding an "if (FALSE)"because this
948            stuff is not working properly and could cause SIGSEVs, for
949            example one send queue will be created for each connection
950            specific SMTP server, so when tinymail asks for the outbox
951            it will return NULL because there is no outbox folder for
952            this specific transport accounts, and it's a must that the
953            send queue returns an outbox */
954         if (TRUE)
955                 /* Add connection-specific transport accounts */
956                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
957         
958         /* This is a singleton, so it does not need to be unrefed. */
959         if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
960                 /* It is mounted: */
961                 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */); 
962         }
963         
964         return MODEST_TNY_ACCOUNT_STORE(obj);
965 }
966
967 static void
968 modest_tny_account_store_get_accounts  (TnyAccountStore *self, 
969                                         TnyList *list,
970                                         TnyGetAccountsRequestType request_type)
971 {
972         ModestTnyAccountStorePrivate *priv;
973         
974         g_return_if_fail (self);
975         g_return_if_fail (TNY_IS_LIST(list));
976         
977         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
978         
979         switch (request_type) {
980         case TNY_ACCOUNT_STORE_BOTH:
981                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
982                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
983                 break;
984         case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
985                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
986                 break;
987         case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
988                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
989                 break;
990         default:
991                 g_return_if_reached ();
992         }
993
994         /* Initialize session. Why do we need this ??? */
995         tny_session_camel_set_initialized (priv->session);
996 }
997
998
999 static const gchar*
1000 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
1001 {
1002         ModestTnyAccountStorePrivate *priv;
1003         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1004         
1005         if (!priv->cache_dir)
1006                 priv->cache_dir = g_build_filename (g_get_home_dir(), 
1007                                                     MODEST_DIR, MODEST_CACHE_DIR, NULL);
1008         return priv->cache_dir;
1009 }
1010
1011
1012 /*
1013  * callers need to unref
1014  */
1015 static TnyDevice*
1016 modest_tny_account_store_get_device (TnyAccountStore *self)
1017 {
1018         ModestTnyAccountStorePrivate *priv;
1019
1020         g_return_val_if_fail (self, NULL);
1021         
1022         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1023         
1024         if (priv->device) 
1025                 return g_object_ref (G_OBJECT(priv->device));
1026         else
1027                 return NULL;
1028 }
1029
1030
1031 static TnyAccount*
1032 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1033 {
1034         return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self), 
1035                                                             MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1036                                                             url_string);
1037 }
1038
1039
1040
1041 static gboolean
1042 modest_tny_account_store_alert (TnyAccountStore *self, 
1043                                 TnyAccount *account, 
1044                                 TnyAlertType type,
1045                                 gboolean question, 
1046                                 GError *error)
1047 {
1048         ModestTransportStoreProtocol proto =
1049                 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN; 
1050         const gchar* server_name = NULL;
1051         gchar *prompt = NULL;
1052         gboolean retval;
1053
1054
1055         g_return_val_if_fail (account, FALSE);
1056         g_return_val_if_fail (error, FALSE);
1057         
1058         /* Get the server name: */
1059         server_name = tny_account_get_hostname (account);
1060
1061         if (account) {
1062                 const gchar *proto_name = tny_account_get_proto (account);
1063                 if (proto_name)
1064                         proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1065                 else {
1066                         g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__, 
1067                                   tny_account_get_id (account));
1068                         return FALSE;
1069                 }
1070         }
1071
1072         switch (error->code) {
1073         case TNY_SYSTEM_ERROR_CANCEL:
1074                 /* Don't show waste the user's time by showing him a dialog telling 
1075                  * him that he has just cancelled something: */
1076                 return TRUE;
1077
1078         case TNY_SERVICE_ERROR_PROTOCOL:
1079                 /* Like a BAD from IMAP (protocol error) */
1080         case TNY_SERVICE_ERROR_LOST_CONNECTION:
1081                 /* Lost the connection with the service */
1082         case TNY_SERVICE_ERROR_UNAVAILABLE:
1083                 /* You must be working online for this operation */
1084         case TNY_SERVICE_ERROR_CONNECT:
1085                 switch (proto) {
1086                 case MODEST_PROTOCOL_STORE_POP:
1087                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1088                                                   server_name);
1089                         break;
1090                 case MODEST_PROTOCOL_STORE_IMAP:
1091                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1092                                                   server_name);
1093                         break;
1094                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1095                         prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1096                                                   server_name);
1097                         break;
1098                 default:
1099                         g_return_val_if_reached (FALSE);
1100                 }
1101                 break;
1102                 
1103         case TNY_SERVICE_ERROR_AUTHENTICATE:
1104                 /* It seems that there's no better error to show with
1105                  * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1106                  * may appear if there's a timeout during auth */
1107                 switch (proto) {
1108                 case MODEST_PROTOCOL_STORE_POP:
1109                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1110                                                   server_name);
1111                         break;
1112                 case MODEST_PROTOCOL_STORE_IMAP:
1113                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1114                                                   server_name);
1115                         break;
1116                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1117                         prompt = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
1118                                                   server_name);
1119                         break;
1120                 default:
1121                         g_return_val_if_reached (FALSE);
1122                 }
1123                 break;
1124                         
1125         case TNY_SERVICE_ERROR_CERTIFICATE:
1126                 /* We'll show the proper dialog later */
1127                 break;
1128
1129         case TNY_SYSTEM_ERROR_MEMORY:
1130                 /* Can't allocate memory for this operation */
1131
1132         case TNY_SERVICE_ERROR_UNKNOWN: 
1133                 return FALSE;                   
1134         default:
1135                 g_return_val_if_reached (FALSE);
1136         }
1137         
1138
1139         if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1140                 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1141                                                                               error->message);
1142         else
1143                 retval = modest_platform_run_alert_dialog (prompt, question);
1144         
1145         if (prompt)
1146                 g_free (prompt);
1147         
1148         return retval;
1149 }
1150
1151
1152 static void
1153 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1154 {
1155         TnyAccountStoreIface *klass;
1156
1157         g_return_if_fail (g);
1158
1159         klass = (TnyAccountStoreIface *)g;
1160
1161         klass->get_accounts =
1162                 modest_tny_account_store_get_accounts;
1163         klass->get_cache_dir =
1164                 modest_tny_account_store_get_cache_dir;
1165         klass->get_device =
1166                 modest_tny_account_store_get_device;
1167         klass->alert =
1168                 modest_tny_account_store_alert;
1169         klass->find_account =
1170                 modest_tny_account_store_find_account_by_url;
1171 }
1172
1173 void
1174 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1175                                             ModestTnyGetPassFunc func)
1176 {
1177         /* not implemented, we use signals */
1178         g_printerr ("modest: set_get_pass_func not implemented\n");
1179 }
1180
1181 TnySessionCamel*
1182 modest_tny_account_store_get_session  (TnyAccountStore *self)
1183 {
1184         g_return_val_if_fail (self, NULL);
1185         return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1186 }
1187
1188 static TnyAccount*
1189 get_tny_account_by (TnyList *accounts,
1190                     ModestTnyAccountStoreQueryType type,
1191                     const gchar *str)
1192 {
1193         TnyIterator *iter = NULL;
1194         gboolean found = FALSE;
1195         TnyAccount *retval = NULL;
1196
1197         g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1198
1199         if (tny_list_get_length(accounts) == 0) {
1200                 g_warning ("%s: account list is empty", __FUNCTION__);
1201                 return NULL;
1202         }
1203         
1204         iter = tny_list_create_iterator (accounts);
1205         while (!tny_iterator_is_done (iter) && !found) {
1206                 TnyAccount *tmp_account = NULL;
1207                 const gchar *val = NULL;
1208
1209                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1210                 if (!TNY_IS_ACCOUNT(tmp_account)) {
1211                         g_warning ("%s: not a valid account", __FUNCTION__);
1212                         tmp_account = NULL;
1213                         break;
1214                 }
1215
1216                 switch (type) {
1217                 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1218                         val = tny_account_get_id (tmp_account);
1219                         break;
1220                 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1221                         val = tny_account_get_url_string (tmp_account);
1222                         break;
1223                 }
1224                 
1225                 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL && 
1226                     tny_account_matches_url_string (tmp_account, str)) {
1227                         retval = g_object_ref (tmp_account);
1228                         found = TRUE;
1229                 } else {
1230                         if (val && str && strcmp (val, str) == 0) {
1231                                 retval = g_object_ref (tmp_account);
1232                                 found = TRUE;
1233                         }
1234                 }
1235                 g_object_unref (tmp_account);
1236                 tny_iterator_next (iter);
1237         }
1238         g_object_unref (iter);
1239
1240         return retval;
1241 }
1242
1243 TnyAccount*
1244 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self, 
1245                                              ModestTnyAccountStoreQueryType type,
1246                                              const gchar *str)
1247 {
1248         TnyAccount *account = NULL;
1249         ModestTnyAccountStorePrivate *priv;     
1250         
1251         g_return_val_if_fail (self, NULL);
1252         g_return_val_if_fail (str, NULL);
1253         
1254         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1255         
1256         /* Search in store accounts */
1257         account = get_tny_account_by (priv->store_accounts, type, str);
1258
1259         /* If we already found something, no need to search the transport accounts */
1260         if (!account) {
1261                 account = get_tny_account_by (priv->transport_accounts, type, str);
1262
1263                 /* If we already found something, no need to search the
1264                    per-account outbox accounts */
1265                 if (!account)
1266                         account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1267         }
1268
1269         /* Warn if nothing was found. This is generally unusual. */
1270         if (!account) {
1271                 g_warning("%s: Failed to find account with %s=%s\n", 
1272                           __FUNCTION__, 
1273                           (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",                     
1274                           str);
1275         }
1276
1277         /* Returns a new reference to the account if found */   
1278         return account;
1279 }
1280
1281
1282 TnyAccount*
1283 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1284                                              const gchar *account_name,
1285                                              TnyAccountType type)
1286 {
1287         ModestTnyAccountStorePrivate *priv = NULL;
1288         TnyAccount *retval = NULL;
1289         TnyList *account_list = NULL;
1290         TnyIterator *iter = NULL;
1291         gboolean found;
1292
1293         g_return_val_if_fail (self, NULL);
1294         g_return_val_if_fail (account_name, NULL);
1295         g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || 
1296                               type == TNY_ACCOUNT_TYPE_TRANSPORT,
1297                               NULL);
1298         
1299         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1300
1301         account_list = (type == TNY_ACCOUNT_TYPE_STORE) ? 
1302                 priv->store_accounts : 
1303                 priv->transport_accounts;
1304
1305         if (!account_list) {
1306                 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__, 
1307                         (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1308                 return NULL;
1309         }
1310         
1311         /* Look for the server account */
1312         found = FALSE;
1313         iter = tny_list_create_iterator (account_list);
1314         while (!tny_iterator_is_done (iter) && !found) {
1315                 const gchar *modest_acc_name;
1316                 TnyAccount *tmp_account;
1317
1318                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1319                 modest_acc_name = 
1320                         modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1321                 
1322                 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1323                         found = TRUE;
1324                         retval = g_object_ref (tmp_account);
1325                 }
1326                 /* Free and continue */
1327                 g_object_unref (tmp_account);
1328                 tny_iterator_next (iter);
1329         }
1330         g_object_unref (iter);
1331
1332         if (!found) {
1333                 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1334                             "Number of server accounts of this type=%d\n", __FUNCTION__,
1335                             (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1336                             account_name, tny_list_get_length (account_list));
1337         }
1338
1339         /* Returns a new reference */
1340         return retval;
1341 }
1342
1343 TnyAccount*
1344 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1345         ModestTnyAccountStore *self, const gchar *account_name)
1346 {
1347         TnyDevice *device;
1348
1349         g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1350         g_return_val_if_fail (account_name, NULL);
1351
1352         /* Get the current connection: */
1353         device = modest_runtime_get_device ();
1354
1355         if (!device) {
1356                 g_warning ("%s: could not get device", __FUNCTION__);
1357                 return NULL;
1358         }
1359                 
1360         if (!tny_device_is_online (device))
1361                 return NULL;
1362         
1363 #ifdef MODEST_HAVE_CONIC
1364         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1365         
1366         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1367         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1368         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1369         if (!iap_id)
1370                 return NULL;
1371                 
1372         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1373         if (!connection)
1374                 return NULL;
1375                 
1376         const gchar *connection_id = con_ic_iap_get_id (connection);
1377         /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1378         if (!connection_id)
1379                 return NULL;
1380         
1381         /*  Get the connection-specific transport acccount, if any: */
1382         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1383
1384         /* Check if this account has connection-specific SMTP enabled */
1385         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1386                 return NULL;
1387         }
1388
1389         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1390                 connection_id);
1391
1392         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1393         if (!server_account_name) {
1394                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1395         }
1396                 
1397         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1398                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1399                                                                            server_account_name);
1400
1401         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1402         g_free (server_account_name);   
1403
1404         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1405         g_object_unref (connection);
1406         
1407         return account;
1408 #else
1409         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1410 #endif /* MODEST_HAVE_CONIC */
1411 }
1412
1413                                                                  
1414 TnyAccount*
1415 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1416                                                                     const gchar *account_name)
1417 {
1418         g_return_val_if_fail (self, NULL);
1419         g_return_val_if_fail (account_name, NULL);
1420
1421         if (!account_name || !self)
1422                 return NULL;
1423         
1424         /*  Get the connection-specific transport acccount, if any: */
1425         /* Note: This gives us a reference: */
1426         TnyAccount *account =
1427                 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1428                         
1429         /* If there is no connection-specific transport account (the common case), 
1430          * just get the regular transport account: */
1431         if (!account) {
1432                 /* The special local folders don't have transport accounts. */
1433                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1434                         account = NULL;
1435                 else {
1436                         /* Note: This gives us a reference: */
1437                         account = modest_tny_account_store_get_server_account (self, account_name, 
1438                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1439                 }
1440         }
1441                         
1442         /* returns a reference. */     
1443         return account;
1444 }
1445
1446 TnyAccount*
1447 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1448 {
1449         TnyAccount *account = NULL;
1450         ModestTnyAccountStorePrivate *priv;
1451         TnyIterator *iter;
1452         gboolean found;
1453
1454         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1455         
1456         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1457
1458         found = FALSE;
1459         iter = tny_list_create_iterator (priv->store_accounts);
1460         while (!tny_iterator_is_done (iter) && !found) {
1461                 TnyAccount *tmp_account;
1462
1463                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1464                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1465                         account = g_object_ref (tmp_account);
1466                         found = TRUE;
1467                 }
1468                 g_object_unref (tmp_account);
1469                 tny_iterator_next (iter);
1470         }
1471         g_object_unref (iter);
1472
1473         /* Returns a new reference to the account */
1474         return account;
1475 }
1476
1477 TnyAccount*
1478 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1479 {
1480         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1481         
1482         /* New reference */
1483         return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1484                                                             MODEST_MMC_ACCOUNT_ID);
1485
1486 }
1487
1488 /*********************************************************************************/
1489 static void
1490 add_existing_accounts (ModestTnyAccountStore *self)
1491 {
1492         GSList *account_names = NULL, *iter = NULL;
1493         ModestTnyAccountStorePrivate *priv = NULL;
1494         
1495         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1496
1497         /* These are account names, not server_account names */
1498         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1499
1500         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1501                 const gchar *account_name = (const gchar*) iter->data;
1502                 
1503                 /* Insert all enabled accounts without notifying */
1504                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1505                         insert_account (self, account_name, FALSE);
1506         }
1507         modest_account_mgr_free_account_names (account_names);
1508 }
1509
1510 static void 
1511 connection_status_changed (TnyAccount *account, 
1512                            TnyConnectionStatus status, 
1513                            gpointer data)
1514 {
1515         /* We do this here and not in the connection policy because we
1516            don't want to do it for every account, just for the
1517            accounts that are interactively added when modest is
1518            runnning */
1519         if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1520                 const gchar *account_name;
1521                 ModestWindow *main_window;
1522                 ModestTnyAccountStorePrivate *priv = NULL;
1523                 
1524                 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (data);
1525
1526                 /* Remove this handler */
1527                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, 
1528                                                                   G_OBJECT (account),
1529                                                                   "connection_status_changed");
1530
1531                 /* Perform a send receive */
1532                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1533                 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1534                 modest_ui_actions_do_send_receive (account_name, FALSE, FALSE, FALSE, main_window);
1535         }
1536 }
1537
1538 static TnyAccount*
1539 create_tny_account (ModestTnyAccountStore *self,
1540                     const gchar *name,
1541                     TnyAccountType type,
1542                     gboolean notify)
1543 {
1544         TnyAccount *account = NULL;
1545         ModestTnyAccountStorePrivate *priv = NULL;
1546         
1547         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1548
1549         account = modest_tny_account_new_from_account (priv->account_mgr,
1550                                                        name, type, 
1551                                                        priv->session,
1552                                                        get_password,
1553                                                        forget_password);
1554
1555         if (account) {
1556                 /* Forget any cached password for the account, so that
1557                    we use a new account if any */
1558                 modest_tny_account_store_forget_password_in_memory (self, 
1559                                                                     tny_account_get_id (account));
1560
1561                 /* Install a signal handler that will refresh the
1562                    account the first time it becomes online. Do this
1563                    only if we're adding a new account while the
1564                    program is running (we do not want to do this
1565                    allways) */
1566                 if (type == TNY_ACCOUNT_TYPE_STORE && notify)
1567                         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, 
1568                                                                        G_OBJECT (account), 
1569                                                                        "connection_status_changed",
1570                                                                        G_CALLBACK (connection_status_changed),
1571                                                                        self);
1572
1573                 /* Set the account store */
1574                 g_object_set_data (G_OBJECT(account), "account_store", self);
1575         } else {
1576                 g_printerr ("modest: failed to create account for %s\n", name);
1577         }
1578
1579         return account;
1580 }
1581
1582
1583 static void
1584 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1585                                                     const gchar *account_name,
1586                                                     TnyAccount *transport_account)
1587 {
1588         TnyList *folders = NULL;
1589         TnyIterator *iter_folders = NULL;
1590         TnyAccount *local_account = NULL, *account_outbox = NULL;
1591         TnyFolder *per_account_outbox = NULL;
1592         ModestTnyAccountStorePrivate *priv = NULL;
1593
1594         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1595
1596         /* Create per account local outbox */
1597         account_outbox = 
1598                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1599                                                                             account_name, 
1600                                                                             priv->session);
1601         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1602
1603         /* Get the outbox folder */
1604         folders = tny_simple_list_new ();
1605         tny_folder_store_get_folders (TNY_FOLDER_STORE (account_outbox), folders, NULL, NULL);
1606         if (tny_list_get_length (folders) != 1) {
1607                 g_warning ("%s: > 1 outbox found (%d)?!", __FUNCTION__,
1608                            tny_list_get_length (folders));
1609         }
1610                         
1611         iter_folders = tny_list_create_iterator (folders);
1612         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1613         g_object_unref (iter_folders);
1614         g_object_unref (folders);
1615         g_object_unref (account_outbox);
1616
1617         /* Add the outbox of the new per-account-local-outbox account
1618            to the global local merged OUTBOX of the local folders
1619            account */
1620         local_account = modest_tny_account_store_get_local_folders_account (self);
1621         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1622                                                                per_account_outbox);
1623         /* Add the pair to the hash table */
1624         g_hash_table_insert (priv->outbox_of_transport,
1625                              transport_account,
1626                              per_account_outbox);
1627         
1628         g_object_unref (local_account);
1629         g_object_unref (per_account_outbox);
1630 }
1631
1632 /*
1633  * This function will be used for both adding new accounts and for the
1634  * initialization. In the initialization we do not want to emit
1635  * signals so notify will be FALSE, in the case of account additions
1636  * we do want to notify the observers
1637  */
1638 static void
1639 insert_account (ModestTnyAccountStore *self,
1640                 const gchar *account,
1641                 gboolean notify)
1642 {
1643         ModestTnyAccountStorePrivate *priv = NULL;
1644         TnyAccount *store_account = NULL, *transport_account = NULL;
1645         
1646         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1647
1648         /* Get the server and the transport account */
1649         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, notify);
1650         if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1651                 g_warning ("%s: failed to create store account", __FUNCTION__);
1652                 return;
1653         }
1654
1655         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, notify);
1656         if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1657                 g_warning ("%s: failed to create transport account", __FUNCTION__);
1658                 g_object_unref (store_account);
1659                 return;
1660         }
1661
1662         /* Add accounts to the lists */
1663         tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1664         tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1665         
1666         /* Create a new pseudo-account with an outbox for this
1667            transport account and add it to the global outbox
1668            in the local account */
1669         add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1670         
1671         /* Notify the observers. We do it after everything is
1672            created */
1673         if (notify) {
1674                 TnyAccount *local_account = NULL;
1675                 
1676                 /* Notify the observers about the new server & transport accounts */
1677                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);   
1678                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1679
1680                 /* Notify that the local account changed */
1681                 local_account = modest_tny_account_store_get_local_folders_account (self);
1682                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1683                 g_object_unref (local_account);
1684         }
1685
1686         /* Frees */
1687         g_object_unref (store_account);
1688         g_object_unref (transport_account);
1689 }
1690
1691 static void
1692 on_account_inserted (ModestAccountMgr *acc_mgr, 
1693                      const gchar *account,
1694                      gpointer user_data)
1695 {
1696         /* Insert the account and notify the observers */
1697         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1698 }
1699
1700 /* This is the callback of the tny_camel_account_set_online called in
1701    on_account_removed to disconnect the account */
1702 static void
1703 on_account_disconnect_when_removing (TnyCamelAccount *account, 
1704                                      gboolean canceled, 
1705                                      GError *err, 
1706                                      gpointer user_data)
1707 {
1708         ModestTnyAccountStore *self;
1709         ModestTnyAccountStorePrivate *priv;
1710
1711         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1712         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1713
1714         /* Remove the connection-status-changed handler if it's still there */
1715         if (modest_signal_mgr_is_connected (priv->sighandlers, 
1716                                             G_OBJECT (account),
1717                                             "connection_status_changed")) {
1718                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, 
1719                                                                   G_OBJECT (account),
1720                                                                   "connection_status_changed");
1721         }
1722
1723         /* Remove it from the list of accounts */
1724         if (TNY_IS_STORE_ACCOUNT (account))
1725                 tny_list_remove (priv->store_accounts, (GObject *) account);
1726         else
1727                 tny_list_remove (priv->transport_accounts, (GObject *) account);
1728
1729         /* Notify the observers */
1730         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 
1731                        0, account);
1732
1733         /* Cancel all pending operations */
1734         tny_account_cancel (TNY_ACCOUNT (account));
1735         
1736         /* Unref the extra reference added by get_server_account */
1737         g_object_unref (account);
1738
1739         /* Clear the cache if it's an store account */
1740         if (TNY_IS_STORE_ACCOUNT (account))
1741                 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1742 }
1743
1744 static void
1745 on_account_removed (ModestAccountMgr *acc_mgr, 
1746                     const gchar *account,
1747                     gpointer user_data)
1748 {
1749         TnyAccount *store_account = NULL, *transport_account = NULL;
1750         ModestTnyAccountStore *self;
1751         ModestTnyAccountStorePrivate *priv;
1752         
1753         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1754         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1755
1756         /* Get the server and the transport account */
1757         store_account = 
1758                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1759         transport_account = 
1760                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1761         
1762         /* If there was any problem creating the account, for example,
1763            with the configuration system this could not exist */
1764         if (store_account) {
1765                 /* Cancel all pending operations */
1766                 tny_account_cancel (TNY_ACCOUNT (store_account));
1767
1768                 /* Disconnect before deleting the cache, because the
1769                    disconnection will rewrite the cache to the
1770                    disk */
1771                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1772                                               on_account_disconnect_when_removing, self);
1773         } else {
1774                 g_warning ("There is no store account for account %s\n", account);
1775         }
1776
1777         /* If there was any problem creating the account, for example,
1778            with the configuration system this could not exist */
1779         if (transport_account) {
1780                 TnyAccount *local_account = NULL;
1781                 TnyFolder *outbox = NULL;
1782                 ModestTnyAccountStorePrivate *priv = NULL;
1783         
1784                 /* Remove the OUTBOX of the account from the global outbox */
1785                 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1786                 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1787
1788                 if (TNY_IS_FOLDER (outbox)) {
1789                         local_account = modest_tny_account_store_get_local_folders_account (self);
1790                         modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1791                                                                                     outbox);
1792                         g_hash_table_remove (priv->outbox_of_transport, transport_account);
1793
1794                         /* Notify the change in the local account */
1795                         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1796                         g_object_unref (local_account);
1797                 } else {
1798                         g_warning ("Removing a transport account that has no outbox");
1799                 }
1800
1801                 /* Cancel all pending operations */
1802                 tny_account_cancel (TNY_ACCOUNT (transport_account));
1803
1804                 /* Disconnect and notify the observers. The callback will free the reference */
1805                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1806                                               on_account_disconnect_when_removing, self);
1807         } else {
1808                 g_warning ("There is no transport account for account %s\n", account);
1809         }
1810 }
1811
1812 TnyTransportAccount *
1813 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1814                                                                     const gchar *name)
1815 {
1816         ModestTnyAccountStorePrivate *priv = NULL;
1817         TnyAccount * tny_account = NULL;
1818
1819         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1820
1821         /* Add the account: */
1822         tny_account = 
1823                 modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1824                                                                  priv->session, 
1825                                                                  name,
1826                                                                  get_password,
1827                                                                  forget_password);
1828         if (tny_account) {
1829                 g_object_set_data (G_OBJECT(tny_account), 
1830                                    "account_store", 
1831                                    (gpointer)self);
1832                 
1833                 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1834                 add_outbox_from_transport_account_to_global_outbox (self, 
1835                                                                     name, 
1836                                                                     tny_account);
1837                 
1838         } else
1839                 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1840                             name);
1841
1842         return TNY_TRANSPORT_ACCOUNT (tny_account);
1843 }
1844
1845
1846 static void
1847 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1848 {
1849         ModestTnyAccountStorePrivate *priv = NULL;
1850         GSList *list_specifics = NULL, *iter = NULL;
1851
1852         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1853
1854         ModestConf *conf = modest_runtime_get_conf ();
1855
1856         GError *err = NULL;
1857         list_specifics = modest_conf_get_list (conf,
1858                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1859                                                MODEST_CONF_VALUE_STRING, &err);
1860         if (err) {
1861                 g_printerr ("modest: %s: error getting list: %s\n.", __FUNCTION__, err->message);
1862                 g_error_free (err);
1863                 err = NULL;
1864         }
1865                                 
1866         /* Look at each connection-specific transport account for the 
1867          * modest account: */
1868         iter = list_specifics;
1869         while (iter) {
1870                 /* The list alternates between the connection name and the transport name: */
1871                 iter = g_slist_next (iter);
1872                 if (iter) {
1873                         const gchar* transport_account_name = (const gchar*) (iter->data);
1874                         TnyTransportAccount * account = NULL;
1875                         account = modest_tny_account_store_new_connection_specific_transport_account (
1876                                 self, transport_account_name);
1877                         if (account)
1878                                 g_object_unref (account);
1879                 }                               
1880                 iter = g_slist_next (iter);
1881         }
1882 }
1883
1884 TnyMsg *
1885 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self, 
1886                                                const gchar *uri,
1887                                                TnyAccount **ac_out)
1888 {
1889         TnyIterator *acc_iter;
1890         ModestTnyAccountStorePrivate *priv;
1891         TnyMsg *msg = NULL;
1892         TnyAccount *msg_account = NULL;
1893
1894         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1895         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1896
1897         acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
1898         while (!msg && !tny_iterator_is_done (acc_iter)) {
1899                 TnyList *folders = tny_simple_list_new ();
1900                 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
1901                 TnyIterator *folders_iter = NULL;
1902
1903                 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
1904                 folders_iter = tny_list_create_iterator (folders);
1905
1906                 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
1907                         TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
1908                         msg = tny_folder_find_msg (folder, uri, NULL);
1909
1910                         if (msg)
1911                                 msg_account = g_object_ref (account);
1912
1913                         g_object_unref (folder);
1914                         tny_iterator_next (folders_iter);
1915                 }
1916                 g_object_unref (folders_iter);
1917
1918                 g_object_unref (folders);
1919                 g_object_unref (account);
1920                 tny_iterator_next (acc_iter);
1921         }
1922
1923         g_object_unref (acc_iter);
1924
1925         if (ac_out != NULL)
1926                 *ac_out = msg_account;
1927
1928         return msg;
1929 }
1930
1931 TnyTransportAccount *
1932 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
1933 {
1934         TnyIterator *acc_iter;
1935         ModestTnyAccountStorePrivate *priv;
1936         TnyTransportAccount *header_acc = NULL;
1937         const gchar *msg_id;
1938
1939         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1940         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
1941         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1942
1943         msg_id = modest_tny_send_queue_get_msg_id (header);
1944         acc_iter = tny_list_create_iterator (priv->transport_accounts);
1945         while (!header_acc && !tny_iterator_is_done (acc_iter)) {
1946                 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
1947                 ModestTnySendQueue *send_queue;
1948                 ModestTnySendQueueStatus status;
1949                 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account));
1950                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
1951                 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
1952                         header_acc = g_object_ref(account);
1953                 }
1954                 g_object_unref (account);
1955                 tny_iterator_next (acc_iter);
1956         }
1957         g_object_unref(acc_iter);
1958
1959         /* New reference */
1960         return header_acc;
1961 }
1962
1963 GtkWidget *
1964 modest_tny_account_store_show_account_settings_dialog (ModestTnyAccountStore *self,
1965                                                       const gchar *account_name)
1966 {
1967         ModestTnyAccountStorePrivate *priv;
1968         gpointer dialog_as_gpointer = NULL;
1969         gboolean found;
1970
1971         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1972         found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
1973                                               account_name, NULL, (gpointer*)&dialog_as_gpointer);
1974
1975         if (found)
1976                 return (GtkWidget *) dialog_as_gpointer;
1977         else {
1978                 ModestAccountSettings *settings;
1979                 GtkWidget *dialog;
1980                 dialog = (GtkWidget *) modest_account_settings_dialog_new ();
1981                 settings = modest_account_mgr_load_account_settings (priv->account_mgr, account_name);
1982                 modest_account_settings_dialog_set_account (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog), settings);
1983                 g_object_unref (settings);
1984                 modest_account_settings_dialog_switch_to_user_info (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
1985                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
1986                 
1987                 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (account_name), dialog);
1988                 
1989                 g_signal_connect (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide), 
1990                                   g_strdup (account_name));
1991                         
1992                 /* Show it and delete it when it closes: */
1993                 g_signal_connect_swapped (dialog, 
1994                                           "response", 
1995                                           G_CALLBACK (gtk_widget_destroy), 
1996                                           dialog);
1997                 gtk_widget_show (GTK_WIDGET (dialog));
1998
1999                 return dialog;
2000         }
2001         
2002 }