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