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