* Dim the "Move to" icon and menu entry when a message is
[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-account.h>
34 #include <tny-account-store.h>
35 #include <tny-store-account.h>
36 #include <tny-error.h>
37 #include <tny-transport-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-account-store.h>
40 #include <tny-camel-transport-account.h>
41 #include <tny-camel-imap-store-account.h>
42 #include <tny-camel-pop-store-account.h>
43
44 #include <modest-runtime.h>
45 #include <modest-marshal.h>
46 #include <modest-protocol-info.h>
47 #include <modest-local-folder-info.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-local-folders-account.h>
50 #include <modest-account-mgr.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <widgets/modest-window-mgr.h>
53 #include <modest-account-settings-dialog.h>
54 #include <maemo/modest-maemo-utils.h>
55 #include <modest-signal-mgr.h>
56
57 #include "modest-tny-account-store.h"
58 #include "modest-tny-platform-factory.h"
59 #include <tny-gtk-lockable.h>
60 #include <camel/camel.h>
61 #include <modest-platform.h>
62
63 #ifdef MODEST_PLATFORM_MAEMO
64 #include <tny-maemo-conic-device.h>
65 #endif
66
67 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
68
69 /* 'private'/'protected' functions */
70 static void    modest_tny_account_store_class_init   (ModestTnyAccountStoreClass *klass);
71 static void    modest_tny_account_store_finalize     (GObject *obj);
72 static void    modest_tny_account_store_instance_init (ModestTnyAccountStore *obj);
73 static void    modest_tny_account_store_init          (gpointer g, gpointer iface_data);
74 static void    modest_tny_account_store_base_init     (gpointer g_class);
75
76 static void    on_account_inserted         (ModestAccountMgr *acc_mgr, 
77                                             const gchar *account,
78                                             gpointer user_data);
79
80 static void    add_existing_accounts       (ModestTnyAccountStore *self);
81
82 static void    insert_account              (ModestTnyAccountStore *self,
83                                             const gchar *account,
84                                             gboolean notify);
85
86 static void    on_account_removed          (ModestAccountMgr *acc_mgr, 
87                                             const gchar *account,
88                                             gpointer user_data);
89
90 static gchar*  get_password                (TnyAccount *account, 
91                                             const gchar * prompt_not_used, 
92                                             gboolean *cancel);
93
94 static void    forget_password             (TnyAccount *account);
95
96 static void    on_vfs_volume_mounted       (GnomeVFSVolumeMonitor *volume_monitor, 
97                                             GnomeVFSVolume *volume, 
98                                             gpointer user_data);
99
100 static void    on_vfs_volume_unmounted     (GnomeVFSVolumeMonitor *volume_monitor, 
101                                             GnomeVFSVolume *volume, 
102                                             gpointer user_data);
103
104 static void    modest_tny_account_store_forget_password_in_memory (ModestTnyAccountStore *self, 
105                                                                    const gchar *server_account_name);
106
107 static void    add_connection_specific_transport_accounts         (ModestTnyAccountStore *self);
108
109 /* list my signals */
110 enum {
111         ACCOUNT_CHANGED_SIGNAL,
112         ACCOUNT_INSERTED_SIGNAL,
113         ACCOUNT_REMOVED_SIGNAL,
114
115         PASSWORD_REQUESTED_SIGNAL,
116         LAST_SIGNAL
117 };
118
119 typedef struct _ModestTnyAccountStorePrivate ModestTnyAccountStorePrivate;
120 struct _ModestTnyAccountStorePrivate {
121         gchar              *cache_dir;  
122         GHashTable         *password_hash;
123         GHashTable         *account_settings_dialog_hash;
124         ModestAccountMgr   *account_mgr;
125         TnySessionCamel    *session;
126         TnyDevice          *device;
127
128         GSList *sighandlers;
129         
130         /* We cache the lists of accounts here */
131         TnyList             *store_accounts;
132         TnyList             *transport_accounts;
133         TnyList             *store_accounts_outboxes;
134         
135         /* Matches transport accounts and outbox folder */
136         GHashTable          *outbox_of_transport;
137 };
138
139 #define MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
140                                                       MODEST_TYPE_TNY_ACCOUNT_STORE, \
141                                                       ModestTnyAccountStorePrivate))
142
143 /* globals */
144 static GObjectClass *parent_class = NULL;
145
146 static guint signals[LAST_SIGNAL] = {0};
147
148 GType
149 modest_tny_account_store_get_type (void)
150 {
151         static GType my_type = 0;
152
153         if (!my_type) {
154                 static const GTypeInfo my_info = {
155                         sizeof(ModestTnyAccountStoreClass),
156                         modest_tny_account_store_base_init,     /* base init */
157                         NULL,           /* base finalize */
158                         (GClassInitFunc) modest_tny_account_store_class_init,
159                         NULL,           /* class finalize */
160                         NULL,           /* class data */
161                         sizeof(ModestTnyAccountStore),
162                         0,              /* n_preallocs */
163                         (GInstanceInitFunc) modest_tny_account_store_instance_init,
164                         NULL
165                 };
166
167                 static const GInterfaceInfo iface_info = {
168                         (GInterfaceInitFunc) modest_tny_account_store_init,
169                         NULL,         /* interface_finalize */
170                         NULL          /* interface_data */
171                 };
172                 /* hack hack */
173                 my_type = g_type_register_static (G_TYPE_OBJECT,
174                                                   "ModestTnyAccountStore",
175                                                   &my_info, 0);
176                 g_type_add_interface_static (my_type, TNY_TYPE_ACCOUNT_STORE,
177                                              &iface_info);
178         }
179         return my_type;
180 }
181
182
183 static void
184 modest_tny_account_store_base_init (gpointer g_class)
185 {
186         static gboolean tny_account_store_initialized = FALSE;
187
188         if (!tny_account_store_initialized) {
189
190                 signals[ACCOUNT_CHANGED_SIGNAL] =
191                         g_signal_new ("account_changed",
192                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
193                                       G_SIGNAL_RUN_FIRST,
194                                       G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_changed),
195                                       NULL, NULL,
196                                       g_cclosure_marshal_VOID__OBJECT,
197                                       G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
198
199                 signals[ACCOUNT_INSERTED_SIGNAL] =
200                         g_signal_new ("account_inserted",
201                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
202                                       G_SIGNAL_RUN_FIRST,
203                                       G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_inserted),
204                                       NULL, NULL,
205                                       g_cclosure_marshal_VOID__OBJECT,
206                                       G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
207                 
208                 signals[ACCOUNT_REMOVED_SIGNAL] =
209                         g_signal_new ("account_removed",
210                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
211                                       G_SIGNAL_RUN_FIRST,
212                                       G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_removed),
213                                       NULL, NULL,
214                                       g_cclosure_marshal_VOID__OBJECT,
215                                       G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
216                 
217 /*              signals[TNY_ACCOUNT_STORE_CONNECTING_FINISHED] = */
218 /*                      g_signal_new ("connecting_finished", */
219 /*                                    TNY_TYPE_ACCOUNT_STORE, */
220 /*                                    G_SIGNAL_RUN_FIRST, */
221 /*                                    G_STRUCT_OFFSET (TnyAccountStoreIface, connecting_finished), */
222 /*                                    NULL, NULL, */
223 /*                                    g_cclosure_marshal_VOID__VOID,  */
224 /*                                    G_TYPE_NONE, 0); */
225
226                 signals[PASSWORD_REQUESTED_SIGNAL] =
227                         g_signal_new ("password_requested",
228                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
229                                       G_SIGNAL_RUN_FIRST,
230                                       G_STRUCT_OFFSET(ModestTnyAccountStoreClass, password_requested),
231                                       NULL, NULL,
232                                       modest_marshal_VOID__STRING_POINTER_POINTER_POINTER_POINTER,
233                                       G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
234                                       G_TYPE_POINTER);          
235
236                 tny_account_store_initialized = TRUE;
237         }
238 }
239
240
241 static void
242 modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass)
243 {
244         GObjectClass *gobject_class;
245         gobject_class = (GObjectClass*) klass;
246
247         parent_class            = g_type_class_peek_parent (klass);
248         gobject_class->finalize = modest_tny_account_store_finalize;
249
250         g_type_class_add_private (gobject_class,
251                                   sizeof(ModestTnyAccountStorePrivate));
252 }
253      
254 static void
255 modest_tny_account_store_instance_init (ModestTnyAccountStore *obj)
256 {
257         GnomeVFSVolumeMonitor* monitor = NULL;
258         ModestTnyAccountStorePrivate *priv;
259
260         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
261
262         priv->cache_dir              = NULL;
263         priv->account_mgr            = NULL;
264         priv->session                = NULL;
265         priv->device                 = NULL;
266         priv->sighandlers            = NULL;
267         
268         priv->outbox_of_transport = g_hash_table_new_full (g_direct_hash,
269                                                            g_direct_equal,
270                                                            NULL,
271                                                            NULL);
272
273         /* An in-memory store of passwords, 
274          * for passwords that are not remembered in the configuration,
275          * so they need to be asked for from the user once in each session:
276          */
277         priv->password_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
278                                                      g_free, g_free);
279                                                              
280         /* A hash-map of modest account names to dialog pointers,
281          * so we can avoid showing the account settings twice for the same modest account: */                                 
282         priv->account_settings_dialog_hash = g_hash_table_new_full (g_str_hash, g_str_equal, 
283                                                                     g_free, NULL);
284         
285         /* Respond to volume mounts and unmounts, such 
286          * as the insertion/removal of the memory card: */
287         /* This is a singleton, so it does not need to be unrefed. */
288         monitor = gnome_vfs_get_volume_monitor();
289
290         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, 
291                                                        G_OBJECT(monitor), 
292                                                        "volume-mounted",
293                                                        G_CALLBACK(on_vfs_volume_mounted),
294                                                        obj);
295         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, 
296                                                        G_OBJECT(monitor), "volume-unmounted",
297                                                        G_CALLBACK(on_vfs_volume_unmounted),
298                                                        obj);
299 }
300
301 /* disconnect the list of TnyAccounts */
302 static void
303 account_disconnect (TnyAccount *account)
304 {
305         g_return_if_fail (account && TNY_IS_ACCOUNT(account));
306         tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, NULL, NULL);
307 }
308
309
310 /* disconnect the list of TnyAccounts */
311 static void
312 account_verify_last_ref (TnyAccount *account, const gchar *str)
313 {
314         gchar *txt;
315
316         g_return_if_fail (account && TNY_IS_ACCOUNT(account));
317
318         txt = g_strdup_printf ("%s: %s", str ? str : "?", tny_account_get_name(account));
319         modest_runtime_verify_object_last_ref(G_OBJECT(account),txt);
320         g_free (txt);
321 }
322
323
324
325
326 static void
327 foreach_account_append_to_list (gpointer data, 
328                                 gpointer user_data)
329 {
330         TnyList *list;
331         
332         list = TNY_LIST (user_data);
333         tny_list_append (list, G_OBJECT (data));
334 }
335
336 /********************************************************************/
337 /*           Control the state of the MMC local account             */
338 /********************************************************************/
339
340 /** Only call this if the memory card is really mounted.
341  */ 
342 static void
343 add_mmc_account(ModestTnyAccountStore *self, gboolean emit_insert_signal)
344 {
345         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
346         g_return_if_fail (priv->session);
347         
348         TnyAccount *mmc_account = modest_tny_account_new_for_local_folders (priv->account_mgr, 
349                                                                         priv->session, 
350                                                                         MODEST_MCC1_VOLUMEPATH);
351
352         /* Add to the list of store accounts */
353         tny_list_append (priv->store_accounts, G_OBJECT (mmc_account));
354
355         if (emit_insert_signal) {
356                 g_signal_emit (G_OBJECT (self), 
357                                signals [ACCOUNT_INSERTED_SIGNAL],
358                                0, mmc_account);
359         }
360
361         /* Free */
362         g_object_unref (mmc_account);
363 }
364
365 static void
366 on_vfs_volume_mounted(GnomeVFSVolumeMonitor *volume_monitor, 
367                       GnomeVFSVolume *volume, 
368                       gpointer user_data)
369 {
370         ModestTnyAccountStore *self;
371         ModestTnyAccountStorePrivate *priv;
372  
373         gchar *uri = NULL;
374
375         self = MODEST_TNY_ACCOUNT_STORE(user_data);
376         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
377         
378         /* Check whether this was the external MMC1 card: */
379         uri = gnome_vfs_volume_get_activation_uri (volume);
380
381         if (uri && (!strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI))) {
382                 add_mmc_account (self, TRUE /* emit the insert signal. */);
383         }
384         
385         g_free (uri);
386 }
387
388 static void
389 on_vfs_volume_unmounted(GnomeVFSVolumeMonitor *volume_monitor, 
390                         GnomeVFSVolume *volume, 
391                         gpointer user_data)
392 {
393         ModestTnyAccountStore *self;
394         ModestTnyAccountStorePrivate *priv;
395         gchar *uri = NULL;
396
397         self = MODEST_TNY_ACCOUNT_STORE(user_data);
398         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
399         
400         /* Check whether this was the external MMC1 card: */
401         uri = gnome_vfs_volume_get_activation_uri (volume);
402         if (uri && (strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI) == 0)) {
403                 TnyAccount *mmc_account = NULL;
404                 gboolean found = FALSE;
405                 TnyIterator *iter = NULL;
406
407                 iter = tny_list_create_iterator (priv->store_accounts);
408                 while (!tny_iterator_is_done (iter) && !found) {
409                         TnyAccount *account;
410
411                         account = TNY_ACCOUNT (tny_iterator_get_current (iter));
412                         if (modest_tny_account_is_memory_card_account (account)) {
413                                 found = TRUE;
414                                 mmc_account = g_object_ref (account);
415                         }
416                         g_object_unref (account);
417                         tny_iterator_next (iter);
418                 }
419                 g_object_unref (iter);
420
421                 if (found) {
422                         /* Remove from the list */
423                         tny_list_remove (priv->store_accounts, G_OBJECT (mmc_account));
424                         
425                         /* Notify observers */
426                         g_signal_emit (G_OBJECT (self),
427                                        signals [ACCOUNT_REMOVED_SIGNAL],
428                                        0, mmc_account);
429
430                         g_object_unref (mmc_account);
431                 } else {
432                         g_warning ("%s: there was no store account for the unmounted MMC",
433                                    __FUNCTION__);
434                 }
435         }
436         g_free (uri);
437 }
438
439 /**
440  * modest_tny_account_store_forget_password_in_memory
441  * @self: a TnyAccountStore instance
442  * @account: A server account.
443  * 
444  * Forget any password stored in memory for this account.
445  * For instance, this should be called when the user has changed the password in the account settings.
446  */
447 static void
448 modest_tny_account_store_forget_password_in_memory (ModestTnyAccountStore *self, const gchar * server_account_name)
449 {
450         /* printf ("DEBUG: %s\n", __FUNCTION__); */
451         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
452
453         if (server_account_name && priv->password_hash) {
454                 g_hash_table_remove (priv->password_hash, server_account_name);
455         }
456 }
457
458 static void
459 on_account_changed (ModestAccountMgr *acc_mgr, 
460                     const gchar *account_name, 
461                     TnyAccountType account_type,
462                     gpointer user_data)
463 {
464         ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
465         ModestTnyAccountStorePrivate *priv;
466         TnyList* account_list;
467         gboolean found = FALSE;
468         TnyIterator *iter = NULL;
469
470         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
471         account_list = (account_type == TNY_ACCOUNT_TYPE_STORE ? 
472                         priv->store_accounts : 
473                         priv->transport_accounts);
474         
475         iter = tny_list_create_iterator (account_list);
476         while (!tny_iterator_is_done (iter) && !found) {
477                 TnyAccount *tny_account;
478                 tny_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
479                 if (tny_account) {
480                         TnyConnectionStatus conn_status = tny_account_get_connection_status (tny_account);
481
482                         if (conn_status != TNY_CONNECTION_STATUS_RECONNECTING &&
483                             conn_status != TNY_CONNECTION_STATUS_INIT) {
484                                 if (!strcmp (tny_account_get_id (tny_account), account_name)) {
485                                         found = TRUE;
486                                         modest_tny_account_update_from_account (tny_account);
487                                         g_signal_emit (G_OBJECT(self), signals[ACCOUNT_CHANGED_SIGNAL], 0, tny_account);
488                                 }
489                         }
490                         g_object_unref (tny_account);
491                 }
492                 tny_iterator_next (iter);
493         }
494
495         if (iter)
496                 g_object_unref (iter);
497 }
498
499 static void 
500 on_account_settings_hide (GtkWidget *widget, gpointer user_data)
501 {
502         TnyAccount *account = (TnyAccount*)user_data;
503         
504         /* This is easier than using a struct for the user_data: */
505         ModestTnyAccountStore *self = modest_runtime_get_account_store();
506         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
507         
508         const gchar *modest_account_name = 
509                         modest_tny_account_get_parent_modest_account_name_for_server_account (account);
510         if (modest_account_name)
511                 g_hash_table_remove (priv->account_settings_dialog_hash, modest_account_name);
512 }
513
514 static void 
515 show_password_warning_only ()
516 {
517         ModestWindow *main_window = 
518                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE); /* don't create */
519         
520         /* Show an explanatory temporary banner: */
521         if (main_window) 
522                 hildon_banner_show_information ( 
523                         GTK_WIDGET(main_window), NULL, _("mcen_ib_username_pw_incorrect"));
524         else
525                 g_warning ("%s: %s", __FUNCTION__, _("mcen_ib_username_pw_incorrect"));
526 }
527
528
529 static void 
530 show_wrong_password_dialog (TnyAccount *account)
531
532         /* This is easier than using a struct for the user_data: */
533         ModestTnyAccountStore *self = modest_runtime_get_account_store();
534         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
535         ModestWindow *main_window;
536         const gchar *modest_account_name;
537
538         main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
539                                                          FALSE); /* don't create */
540         if (!main_window) {
541                 g_warning ("%s: password was wrong; ignoring because no main window", __FUNCTION__);
542                 return;
543         }
544
545         modest_account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
546         if (!modest_account_name) {
547                 g_warning ("%s: modest_tny_account_get_parent_modest_account_name_for_server_account() failed.\n", 
548                         __FUNCTION__);
549         }
550         
551         /* Check whether this window is already open,
552          * for instance because of a previous get_password() call: 
553          */
554         gpointer dialog_as_gpointer = NULL;
555         gboolean found = FALSE;
556         if (priv->account_settings_dialog_hash) {
557                 found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
558                         modest_account_name, NULL, (gpointer*)&dialog_as_gpointer);
559         }
560         ModestAccountSettingsDialog *dialog = dialog_as_gpointer;
561                                         
562         gboolean created_dialog = FALSE;
563         if (!found || !dialog) {
564                 dialog = modest_account_settings_dialog_new ();
565                 modest_account_settings_dialog_set_account_name (dialog, modest_account_name);
566                 modest_account_settings_dialog_switch_to_user_info (dialog);
567                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
568                 
569                 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (modest_account_name), dialog);
570                 
571                 created_dialog = TRUE;
572         }
573         
574         /* Show an explanatory temporary banner: */
575         hildon_banner_show_information (GTK_WIDGET(dialog), NULL, _("mcen_ib_username_pw_incorrect"));
576                 
577         if (created_dialog) {
578                 /* Forget it when it closes: */
579                 g_signal_connect_object (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide), 
580                         account, 0);
581                         
582                 /* Show it and delete it when it closes: */
583                 modest_maemo_show_dialog_and_forget (GTK_WINDOW (main_window), GTK_DIALOG (dialog));
584         }
585         else {
586                 /* Just show it instead of showing it and deleting it when it closes,
587                  * though it is probably open already: */
588                 gtk_window_present (GTK_WINDOW (dialog));
589         }
590 }
591
592
593
594 static void
595 request_password_and_wait (ModestTnyAccountStore *account_store, 
596                                          const gchar* server_account_id,
597                                          gchar **username,
598                                          gchar **password,
599                                          gboolean *cancel, 
600                                          gboolean *remember)
601 {
602         g_signal_emit (G_OBJECT(account_store), signals[PASSWORD_REQUESTED_SIGNAL], 0,
603                        server_account_id, /* server_account_name */
604                        username, password, cancel, remember);
605 }
606
607 /* This callback will be called by Tinymail when it needs the password
608  * from the user or the account settings.
609  * It can also call forget_password() before calling this,
610  * so that we clear wrong passwords out of our account settings.
611  * Note that TnyAccount here will be the server account. */
612 static gchar*
613 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
614 {
615         /* TODO: Settting cancel to FALSE does not actually cancel everything.
616          * We still get multiple requests afterwards, so we end up showing the 
617          * same dialogs repeatedly.
618          */
619          
620         printf ("DEBUG: modest: %s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
621           
622         g_return_val_if_fail (account, NULL);
623           
624         const TnyAccountStore *account_store = NULL;
625         ModestTnyAccountStore *self = NULL;
626         ModestTnyAccountStorePrivate *priv;
627         gchar *username = NULL;
628         gchar *pwd = NULL;
629         gpointer pwd_ptr = NULL;
630         gboolean already_asked = FALSE;
631
632         /* Initialize the output parameter: */
633         if (cancel)
634                 *cancel = FALSE;
635                 
636         const gchar *server_account_name = tny_account_get_id (account);
637         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
638                                                              "account_store"));
639
640         if (!server_account_name || !account_store) {
641                 g_warning ("modest: %s: could not retrieve account_store for account %s",
642                            __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
643                 if (cancel)
644                         *cancel = TRUE;
645                 
646                 return NULL;
647         }
648
649         self = MODEST_TNY_ACCOUNT_STORE (account_store);
650         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
651         
652         /* This hash map stores passwords, including passwords that are not stored in gconf. */
653         /* Is it in the hash? if it's already there, it must be wrong... */
654         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
655                                    * type-punned ptrs...*/
656         already_asked = priv->password_hash && 
657                                 g_hash_table_lookup_extended (priv->password_hash,
658                                                       server_account_name,
659                                                       NULL,
660                                                       (gpointer*)&pwd_ptr);
661                                                       
662         printf ("DEBUG: modest: %s: Already asked = %d\n", __FUNCTION__, already_asked);
663
664         /* If the password is not already there, try ModestConf */
665         if (!already_asked) {
666                 pwd  = modest_account_mgr_get_server_account_password (priv->account_mgr,
667                                                                        server_account_name);
668                 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
669         }
670
671         /* If it was already asked, it must have been wrong, so ask again */
672         if (already_asked || !pwd || strlen(pwd) == 0) {
673                 /* As per the UI spec, if no password was set in the account settings, 
674                  * ask for it now. But if the password is wrong in the account settings, 
675                  * then show a banner and the account settings dialog so it can be corrected:
676                  */
677                 const gboolean settings_have_password = 
678                         modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
679                 printf ("DEBUG: modest: %s: settings_have_password=%d\n", __FUNCTION__, settings_have_password);
680                 if (settings_have_password) {
681                         /* The password must be wrong, so show the account settings dialog so it can be corrected: */
682                         show_wrong_password_dialog (account);
683                         
684                         if (cancel)
685                                 *cancel = TRUE;
686                                 
687                         return NULL;
688                 }
689         
690                 /* we don't have it yet. Get the password from the user */
691                 const gchar* account_id = tny_account_get_id (account);
692                 gboolean remember = FALSE;
693                 pwd = NULL;
694                 
695                 if (already_asked) {
696                         /* Show an info banner, before we show the protected password dialog: */
697                         show_password_warning_only();
698                 }
699                 
700                 request_password_and_wait (self, account_id, 
701                                &username, &pwd, cancel, &remember);
702                 
703                 if (!*cancel) {
704                         /* The password will be returned as the result,
705                          * but we need to tell tinymail about the username too: */
706                         tny_account_set_user (account, username);
707                         
708                         /* Do not save the password in gconf, because
709                          * the UI spec says "The password will never
710                          * be saved in the account": */
711
712                         /* We need to dup the string even knowing that
713                            it's already a dup of the contents of an
714                            entry, because it if it's wrong, then camel
715                            will free it */
716                         g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
717                 } else {
718                         g_hash_table_remove (priv->password_hash, server_account_name);
719                         
720                         g_free (pwd);
721                         pwd = NULL;
722                 }
723
724                 g_free (username);
725                 username = NULL;
726         } else
727                 if (cancel)
728                         *cancel = FALSE;
729  
730     /* printf("  DEBUG: %s: returning %s\n", __FUNCTION__, pwd); */
731         
732         return pwd;
733 }
734
735 void 
736 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
737 {
738         g_return_if_fail (account);
739           
740         ModestTnyAccountStorePrivate *priv;
741         gchar *pwd = NULL;
742         gpointer pwd_ptr = NULL;
743         gboolean already_asked = FALSE;
744                 
745         const gchar *server_account_name = tny_account_get_id (account);
746
747         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
748         
749         /* This hash map stores passwords, including passwords that are not stored in gconf. */
750         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
751                                    * type-punned ptrs...*/
752         already_asked = priv->password_hash && 
753                                 g_hash_table_lookup_extended (priv->password_hash,
754                                                       server_account_name,
755                                                       NULL,
756                                                       (gpointer*)&pwd_ptr);
757
758         if (already_asked) {
759                 g_hash_table_remove (priv->password_hash, server_account_name);         
760                 g_free (pwd);
761                 pwd = NULL;
762         }
763
764         return;
765 }
766
767 /* tinymail calls this if the connection failed due to an incorrect password.
768  * And it seems to call this for any general connection failure. */
769 static void
770 forget_password (TnyAccount *account)
771 {
772         printf ("DEBUG: %s\n", __FUNCTION__);
773         ModestTnyAccountStore *self;
774         ModestTnyAccountStorePrivate *priv;
775         const TnyAccountStore *account_store;
776         gchar *pwd;
777         const gchar *key;
778         
779         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
780                                                              "account_store"));
781         self = MODEST_TNY_ACCOUNT_STORE (account_store);
782         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
783         key  = tny_account_get_id (account);
784
785         /* Do not remove the key, this will allow us to detect that we
786            have already asked for it at least once */
787         pwd = g_hash_table_lookup (priv->password_hash, key);
788         if (pwd) {
789                 memset (pwd, 0, strlen (pwd));
790                 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
791         }
792
793         /* Remove from configuration system */
794         /*
795         modest_account_mgr_unset (priv->account_mgr,
796                                   key, MODEST_ACCOUNT_PASSWORD, TRUE);
797         */
798 }
799
800 static void
801 modest_tny_account_store_finalize (GObject *obj)
802 {
803         ModestTnyAccountStore *self        = MODEST_TNY_ACCOUNT_STORE(obj);
804         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
805
806         g_free (priv->cache_dir);
807         priv->cache_dir = NULL;
808         
809         if (priv->password_hash) {
810                 g_hash_table_destroy (priv->password_hash);
811                 priv->password_hash = NULL;
812         }
813
814         if (priv->account_settings_dialog_hash) {
815                 g_hash_table_destroy (priv->account_settings_dialog_hash);
816                 priv->account_settings_dialog_hash = NULL;
817         }
818
819         if (priv->outbox_of_transport) {
820                 g_hash_table_destroy (priv->outbox_of_transport);
821                 priv->outbox_of_transport = NULL;
822         }
823
824         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
825         priv->sighandlers = NULL;       
826
827         if (priv->account_mgr) {
828                 g_object_unref (G_OBJECT(priv->account_mgr));
829                 priv->account_mgr = NULL;
830         }
831
832         if (priv->device) {
833                 g_object_unref (G_OBJECT(priv->device));
834                 priv->device = NULL;
835         }
836
837         /* Destroy all accounts. Disconnect all accounts before they are destroyed */
838         if (priv->store_accounts) {
839                 tny_list_foreach (priv->store_accounts, (GFunc)account_disconnect, NULL);
840                 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
841                 g_object_unref (priv->store_accounts);
842                 priv->store_accounts = NULL;
843         }
844         
845         if (priv->transport_accounts) {
846                 tny_list_foreach (priv->transport_accounts, (GFunc)account_disconnect, NULL);
847                 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
848                 g_object_unref (priv->transport_accounts);
849                 priv->transport_accounts = NULL;
850         }
851
852         if (priv->store_accounts_outboxes) {
853                 g_object_unref (priv->store_accounts_outboxes);
854                 priv->store_accounts_outboxes = NULL;
855         }
856                 
857         if (priv->session) {
858                 camel_object_unref (CAMEL_OBJECT(priv->session));
859                 priv->session = NULL;
860         }
861         
862         G_OBJECT_CLASS(parent_class)->finalize (obj);
863 }
864
865 gboolean 
866 volume_path_is_mounted (const gchar* path)
867 {
868         g_return_val_if_fail (path, FALSE);
869
870         gboolean result = FALSE;
871         gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
872         g_return_val_if_fail (path_as_uri, FALSE);
873
874         /* Get the monitor singleton: */
875         GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
876
877         /* This seems like a simpler way to do this, but it returns a   
878          * GnomeVFSVolume even if the drive is not mounted: */
879         /*
880         GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor, 
881                 MODEST_MCC1_VOLUMEPATH);
882         if (volume) {
883                 gnome_vfs_volume_unref(volume);
884         }
885         */
886
887         /* Get the mounted volumes from the monitor: */
888         GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
889         GList *iter = list;
890         for (iter = list; iter; iter = g_list_next (iter)) {
891                 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
892                 if (volume) {
893                         /*
894                         char *display_name = 
895                                 gnome_vfs_volume_get_display_name (volume);
896                         printf ("volume display name=%s\n", display_name);
897                         g_free (display_name);
898                         */
899                         
900                         char *uri = 
901                                 gnome_vfs_volume_get_activation_uri (volume);
902                         /* printf ("  uri=%s\n", uri); */
903                         if (uri && (strcmp (uri, path_as_uri) == 0))
904                                 result = TRUE;
905
906                         g_free (uri);
907
908                         gnome_vfs_volume_unref (volume);
909                 }
910         }
911
912         g_list_free (list);
913
914         g_free (path_as_uri);
915
916         return result;
917 }
918
919 ModestTnyAccountStore*
920 modest_tny_account_store_new (ModestAccountMgr *account_mgr, 
921                               TnyDevice *device) 
922 {
923         GObject *obj;
924         ModestTnyAccountStorePrivate *priv;
925         TnyAccount *local_account = NULL;
926         
927         g_return_val_if_fail (account_mgr, NULL);
928         g_return_val_if_fail (device, NULL);
929
930         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
931         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
932
933         priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
934         priv->device = g_object_ref (device);
935         
936         priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
937         if (!priv->session) {
938                 g_warning ("failed to get TnySessionCamel");
939                 return NULL;
940         }
941
942         /* Set the ui locker */ 
943         tny_session_camel_set_ui_locker (priv->session,  tny_gtk_lockable_new ());
944         
945         /* Connect signals */
946         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
947                                                          G_OBJECT(account_mgr), "account_inserted",
948                                                          G_CALLBACK (on_account_inserted), obj);
949         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
950                                                          G_OBJECT(account_mgr), "account_changed",
951                                                          G_CALLBACK (on_account_changed), obj);
952         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
953                                                          G_OBJECT(account_mgr), "account_removed",
954                                                          G_CALLBACK (on_account_removed), obj);
955
956         /* Create the lists of accounts */
957         priv->store_accounts = tny_simple_list_new ();
958         priv->transport_accounts = tny_simple_list_new ();
959         priv->store_accounts_outboxes = tny_simple_list_new ();
960
961         /* Create the local folders account */
962         local_account = 
963                 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
964         tny_list_append (priv->store_accounts, G_OBJECT(local_account));
965         g_object_unref (local_account); 
966
967         /* Add the other remote accounts. Do this after adding the
968            local account, because we need to add our outboxes to the
969            global OUTBOX hosted in the local account */
970         add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
971         
972         /* FIXME: I'm doing this (adding an "if (FALSE)"because this
973            stuff is not working properly and could cause SIGSEVs, for
974            example one send queue will be created for each connection
975            specific SMTP server, so when tinymail asks for the outbox
976            it will return NULL because there is no outbox folder for
977            this specific transport accounts, and it's a must that the
978            send queue returns an outbox */
979         if (TRUE)
980                 /* Add connection-specific transport accounts */
981                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
982         
983         /* This is a singleton, so it does not need to be unrefed. */
984         if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
985                 /* It is mounted: */
986                 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */); 
987         }
988         
989         return MODEST_TNY_ACCOUNT_STORE(obj);
990 }
991
992 static void
993 modest_tny_account_store_get_accounts  (TnyAccountStore *self, 
994                                         TnyList *list,
995                                         TnyGetAccountsRequestType request_type)
996 {
997         ModestTnyAccountStorePrivate *priv;
998         
999         g_return_if_fail (self);
1000         g_return_if_fail (TNY_IS_LIST(list));
1001         
1002         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1003         
1004         switch (request_type) {
1005         case TNY_ACCOUNT_STORE_BOTH:
1006                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1007                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1008                 break;
1009         case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
1010                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1011                 break;
1012         case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
1013                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1014                 break;
1015         default:
1016                 g_return_if_reached ();
1017         }
1018
1019         /* Initialize session. Why do we need this ??? */
1020         tny_session_camel_set_initialized (priv->session);
1021 }
1022
1023
1024 static const gchar*
1025 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
1026 {
1027         ModestTnyAccountStorePrivate *priv;
1028         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1029         
1030         if (!priv->cache_dir)
1031                 priv->cache_dir = g_build_filename (g_get_home_dir(), 
1032                                                     MODEST_DIR, MODEST_CACHE_DIR, NULL);
1033         return priv->cache_dir;
1034 }
1035
1036
1037 /*
1038  * callers need to unref
1039  */
1040 static TnyDevice*
1041 modest_tny_account_store_get_device (TnyAccountStore *self)
1042 {
1043         ModestTnyAccountStorePrivate *priv;
1044
1045         g_return_val_if_fail (self, NULL);
1046         
1047         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1048         
1049         if (priv->device) 
1050                 return g_object_ref (G_OBJECT(priv->device));
1051         else
1052                 return NULL;
1053 }
1054
1055
1056 static TnyAccount*
1057 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1058 {
1059         return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self), 
1060                                                             MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1061                                                             url_string);
1062 }
1063
1064
1065
1066 static gboolean
1067 modest_tny_account_store_alert (TnyAccountStore *self, 
1068                                 TnyAccount *account, 
1069                                 TnyAlertType type,
1070                                 gboolean question, 
1071                                 const GError *error)
1072 {
1073         ModestTransportStoreProtocol proto =
1074                 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN; 
1075         const gchar* server_name = NULL;
1076         gchar *prompt = NULL;
1077         gboolean retval;
1078
1079
1080         g_return_val_if_fail (account, FALSE);
1081         g_return_val_if_fail (error, FALSE);
1082         
1083         if ((error->domain != TNY_ACCOUNT_ERROR) && (error->domain != TNY_ACCOUNT_STORE_ERROR))
1084                 return FALSE;
1085         
1086         /* Get the server name: */
1087         server_name = tny_account_get_hostname (account);
1088
1089         if (account) {
1090                 const gchar *proto_name = tny_account_get_proto (account);
1091                 if (proto_name)
1092                         proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1093                 else {
1094                         g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__, 
1095                                   tny_account_get_id (account));
1096                         return FALSE;
1097                 }
1098         }
1099
1100         switch (error->code) {
1101         case TNY_ACCOUNT_STORE_ERROR_CANCEL_ALERT:
1102         case TNY_ACCOUNT_ERROR_TRY_CONNECT_USER_CANCEL:
1103                 /* Don't show waste the user's time by showing him a dialog telling 
1104                  * him that he has just cancelled something: */
1105                 return TRUE;
1106
1107         case TNY_ACCOUNT_ERROR_TRY_CONNECT_HOST_LOOKUP_FAILED:
1108         case TNY_ACCOUNT_ERROR_TRY_CONNECT_SERVICE_UNAVAILABLE:
1109                 /* TODO: Show the appropriate message, depending on whether it's POP or IMAP: */                
1110                 switch (proto) {
1111                 case MODEST_PROTOCOL_STORE_POP:
1112                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1113                                                   server_name);
1114                         break;
1115                 case MODEST_PROTOCOL_STORE_IMAP:
1116                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1117                                                   server_name);
1118                         break;
1119                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1120                         prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1121                                                   server_name);
1122                         break;
1123                 default:
1124                         g_return_val_if_reached (FALSE);
1125                 }
1126                 break;
1127                 
1128         case TNY_ACCOUNT_ERROR_TRY_CONNECT_AUTHENTICATION_NOT_SUPPORTED:
1129                 /* This is "Secure connection failed", even though the logical
1130                  * ID has _certificate_ in the name: */
1131                 prompt = g_strdup (_("mail_ni_ssl_certificate_error")); 
1132                 break;
1133                         
1134         case TNY_ACCOUNT_ERROR_TRY_CONNECT_CERTIFICATE:
1135                 /* We'll show the proper dialog later */
1136                 break;
1137                 
1138         case TNY_ACCOUNT_ERROR_TRY_CONNECT:
1139                 /* The tinymail camel implementation just sends us this for almost 
1140                  * everything, so we have to guess at the cause.
1141                  * It could be a wrong password, or inability to resolve a hostname, 
1142                  * or lack of network, or incorrect authentication method, or something entirely different: */
1143                 /* TODO: Fix camel to provide specific error codes, and then use the 
1144                  * specific dialog messages from Chapter 12 of the UI spec.
1145                  */
1146         case TNY_ACCOUNT_STORE_ERROR_UNKNOWN_ALERT: 
1147                 return FALSE;                   
1148         default:
1149                 g_return_val_if_reached (FALSE);
1150         }
1151         
1152
1153         if (error->code == TNY_ACCOUNT_ERROR_TRY_CONNECT_CERTIFICATE)
1154                 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1155                                                                               error->message);
1156         else
1157                 retval = modest_platform_run_alert_dialog (prompt, question);
1158         
1159         if (prompt)
1160                 g_free (prompt);
1161         
1162         return retval;
1163 }
1164
1165
1166 static void
1167 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1168 {
1169         TnyAccountStoreIface *klass;
1170
1171         g_return_if_fail (g);
1172
1173         klass = (TnyAccountStoreIface *)g;
1174
1175         klass->get_accounts_func =
1176                 modest_tny_account_store_get_accounts;
1177         klass->get_cache_dir_func =
1178                 modest_tny_account_store_get_cache_dir;
1179         klass->get_device_func =
1180                 modest_tny_account_store_get_device;
1181         klass->alert_func =
1182                 modest_tny_account_store_alert;
1183         klass->find_account_func =
1184                 modest_tny_account_store_find_account_by_url;
1185 }
1186
1187 void
1188 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1189                                             ModestTnyGetPassFunc func)
1190 {
1191         /* not implemented, we use signals */
1192         g_printerr ("modest: set_get_pass_func not implemented\n");
1193 }
1194
1195 TnySessionCamel*
1196 modest_tny_account_store_get_session  (TnyAccountStore *self)
1197 {
1198         g_return_val_if_fail (self, NULL);
1199         return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1200 }
1201
1202 static TnyAccount*
1203 get_tny_account_by (TnyList *accounts,
1204                     ModestTnyAccountStoreQueryType type,
1205                     const gchar *str)
1206 {
1207         TnyIterator *iter = NULL;
1208         gboolean found = FALSE;
1209         TnyAccount *retval = NULL;
1210
1211         iter = tny_list_create_iterator (accounts);
1212         while (!tny_iterator_is_done (iter) && !found) {
1213                 TnyAccount *tmp_account = NULL;
1214                 const gchar *val = NULL;
1215
1216                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1217                 switch (type) {
1218                 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1219                         val = tny_account_get_id (tmp_account);
1220                         break;
1221                 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1222                         val = tny_account_get_url_string (tmp_account);
1223                         break;
1224                 }
1225                 
1226                 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL && 
1227                     tny_account_matches_url_string (tmp_account, str)) {
1228                         retval = g_object_ref (tmp_account);
1229                         found = TRUE;
1230                 } else {
1231                         if (strcmp (val, str) == 0) {
1232                                 retval = g_object_ref (tmp_account);
1233                                 found = TRUE;
1234                         }
1235                 }
1236                 g_object_unref (tmp_account);
1237                 tny_iterator_next (iter);
1238         }
1239         g_object_unref (iter);
1240
1241         return retval;
1242 }
1243
1244 TnyAccount*
1245 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self, 
1246                                              ModestTnyAccountStoreQueryType type,
1247                                              const gchar *str)
1248 {
1249         TnyAccount *account = NULL;
1250         ModestTnyAccountStorePrivate *priv;     
1251         
1252         g_return_val_if_fail (self, NULL);
1253         g_return_val_if_fail (str, NULL);
1254         
1255         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1256         
1257         /* Search in store accounts */
1258         account = get_tny_account_by (priv->store_accounts, type, str);
1259
1260         /* If we already found something, no need to search the transport accounts */
1261         if (!account) {
1262                 account = get_tny_account_by (priv->transport_accounts, type, str);
1263
1264                 /* If we already found something, no need to search the
1265                    per-account outbox accounts */
1266                 if (!account)
1267                         account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1268         }
1269
1270         /* Warn if nothing was found. This is generally unusual. */
1271         if (!account) {
1272                 g_warning("%s: Failed to find account with %s=%s\n", 
1273                           __FUNCTION__, 
1274                           (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",                     
1275                           str);
1276         }
1277
1278         /* Returns a new reference to the account if found */   
1279         return account;
1280 }
1281
1282
1283 TnyAccount*
1284 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1285                                              const gchar *account_name,
1286                                              TnyAccountType type)
1287 {
1288         ModestTnyAccountStorePrivate *priv = NULL;
1289         TnyAccount *retval = NULL;
1290         TnyList *account_list = NULL;
1291         TnyIterator *iter = NULL;
1292         gboolean found;
1293
1294         g_return_val_if_fail (self, NULL);
1295         g_return_val_if_fail (account_name, NULL);
1296         g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || 
1297                               type == TNY_ACCOUNT_TYPE_TRANSPORT,
1298                               NULL);
1299         
1300         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1301
1302         account_list = (type == TNY_ACCOUNT_TYPE_STORE) ? 
1303                 priv->store_accounts : 
1304                 priv->transport_accounts;
1305
1306         if (!account_list) {
1307                 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__, 
1308                         (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1309                 return NULL;
1310         }
1311         
1312         /* Look for the server account */
1313         found = FALSE;
1314         iter = tny_list_create_iterator (account_list);
1315         while (!tny_iterator_is_done (iter) && !found) {
1316                 const gchar *modest_acc_name;
1317                 TnyAccount *tmp_account;
1318
1319                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1320                 modest_acc_name = 
1321                         modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1322                 
1323                 if (!strcmp (account_name, modest_acc_name)) {
1324                         found = TRUE;
1325                         retval = g_object_ref (tmp_account);
1326                 }
1327                 /* Free and continue */
1328                 g_object_unref (tmp_account);
1329                 tny_iterator_next (iter);
1330         }
1331
1332         if (!found) {
1333                 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1334                             "Number of server accounts of this type=%d\n", __FUNCTION__,
1335                             (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1336                             account_name, tny_list_get_length (account_list));
1337         }
1338
1339         /* Returns a new reference */
1340         return retval;
1341 }
1342
1343 TnyAccount*
1344 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (ModestTnyAccountStore *self,
1345                                                                                   const gchar *account_name)
1346 {
1347         /* Get the current connection: */
1348         TnyDevice *device = modest_runtime_get_device ();
1349         
1350         if (!tny_device_is_online (device))
1351                 return NULL;
1352
1353         g_return_val_if_fail (self, NULL);
1354         
1355         
1356 #ifdef MODEST_PLATFORM_MAEMO
1357         g_assert (TNY_IS_MAEMO_CONIC_DEVICE (device));
1358         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1359         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1360         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1361         if (!iap_id)
1362                 return NULL;
1363                 
1364         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1365         if (!connection)
1366                 return NULL;
1367                 
1368         const gchar *connection_name = con_ic_iap_get_name (connection);
1369         /* printf ("DEBUG: %s: connection_name=%s\n", __FUNCTION__, connection_name); */
1370         if (!connection_name)
1371                 return NULL;
1372         
1373         /*  Get the connection-specific transport acccount, if any: */
1374         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1375
1376         /* Check if this account has connection-specific SMTP enabled */
1377         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1378                 return NULL;
1379         }
1380
1381         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1382                 connection_name);
1383
1384         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1385         if (!server_account_name) {
1386                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1387         }
1388                 
1389         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1390                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1391                                                                            server_account_name);
1392
1393         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1394         g_free (server_account_name);   
1395
1396         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1397         g_object_unref (connection);
1398         
1399         return account;
1400 #else
1401         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1402 #endif /* MODEST_PLATFORM_MAEMO */
1403 }
1404
1405                                                                  
1406 TnyAccount*
1407 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1408                                                                     const gchar *account_name)
1409 {
1410         g_return_val_if_fail (self, NULL);
1411         g_return_val_if_fail (account_name, NULL);
1412
1413         if (!account_name || !self)
1414                 return NULL;
1415         
1416         /*  Get the connection-specific transport acccount, if any: */
1417         /* Note: This gives us a reference: */
1418         TnyAccount *account =
1419                 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1420                         
1421         /* If there is no connection-specific transport account (the common case), 
1422          * just get the regular transport account: */
1423         if (!account) {
1424                 /* The special local folders don't have transport accounts. */
1425                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1426                         account = NULL;
1427                 else {
1428                         /* Note: This gives us a reference: */
1429                         account = modest_tny_account_store_get_server_account (self, account_name, 
1430                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1431                 }
1432         }
1433                         
1434         /* returns a reference. */     
1435         return account;
1436 }
1437
1438 TnyAccount*
1439 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1440 {
1441         TnyAccount *account = NULL;
1442         ModestTnyAccountStorePrivate *priv;
1443         TnyIterator *iter;
1444         gboolean found;
1445
1446         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1447         
1448         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1449
1450         found = FALSE;
1451         iter = tny_list_create_iterator (priv->store_accounts);
1452         while (!tny_iterator_is_done (iter) && !found) {
1453                 TnyAccount *tmp_account;
1454
1455                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1456                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1457                         account = g_object_ref (tmp_account);
1458                         found = TRUE;
1459                 }
1460                 g_object_unref (tmp_account);
1461                 tny_iterator_next (iter);
1462         }
1463         g_object_unref (iter);
1464
1465         /* Returns a new reference to the account */
1466         return account;
1467 }
1468
1469 TnyAccount*
1470 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1471 {
1472         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1473         
1474         return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1475                                                             MODEST_MMC_ACCOUNT_ID);
1476
1477 }
1478
1479 /*********************************************************************************/
1480 static void
1481 add_existing_accounts (ModestTnyAccountStore *self)
1482 {
1483         GSList *account_names = NULL, *iter = NULL;
1484         ModestTnyAccountStorePrivate *priv = NULL;
1485         
1486         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1487
1488         /* These are account names, not server_account names */
1489         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1490
1491         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1492                 const gchar *account_name = (const gchar*) iter->data;
1493                 
1494                 /* Insert all enabled accounts without notifying */
1495                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1496                         insert_account (self, account_name, FALSE);
1497         }
1498         modest_account_mgr_free_account_names (account_names);
1499 }
1500
1501 static TnyAccount*
1502 create_tny_account (ModestTnyAccountStore *self,
1503                     const gchar *name,
1504                     TnyAccountType type)
1505 {
1506         TnyAccount *account = NULL;
1507         ModestTnyAccountStorePrivate *priv = NULL;
1508         
1509         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1510
1511         account = modest_tny_account_new_from_account (priv->account_mgr,
1512                                                        name, type, 
1513                                                        priv->session,
1514                                                        get_password,
1515                                                        forget_password);
1516
1517         if (account) {
1518                 /* Forget any cached password for the account, so that
1519                    we use a new account if any */
1520                 modest_tny_account_store_forget_password_in_memory (self, 
1521                                                                     tny_account_get_id (account));
1522                 /* Set the account store */                             
1523                 g_object_set_data (G_OBJECT(account), "account_store", self);
1524         } else {
1525                 g_printerr ("modest: failed to create account for %s\n", name);
1526         }
1527
1528         return account;
1529 }
1530
1531
1532 static void
1533 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1534                                                     const gchar *account_name,
1535                                                     TnyAccount *transport_account)
1536 {
1537         TnyList *folders = NULL;
1538         TnyIterator *iter_folders = NULL;
1539         TnyAccount *local_account = NULL, *account_outbox = NULL;
1540         TnyFolder *per_account_outbox = NULL;
1541         ModestTnyAccountStorePrivate *priv = NULL;
1542
1543         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1544
1545         /* Create per account local outbox */
1546         account_outbox = 
1547                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1548                                                                             account_name, 
1549                                                                             priv->session);
1550         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1551
1552         /* Get the outbox folder */
1553         folders = tny_simple_list_new ();
1554         tny_folder_store_get_folders (TNY_FOLDER_STORE (account_outbox), folders, NULL, NULL);
1555         g_assert (tny_list_get_length (folders) == 1);
1556                 
1557         iter_folders = tny_list_create_iterator (folders);
1558         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1559         g_object_unref (iter_folders);
1560         g_object_unref (folders);
1561         g_object_unref (account_outbox);
1562
1563         /* Add the outbox of the new per-account-local-outbox account
1564            to the global local merged OUTBOX of the local folders
1565            account */
1566         local_account = modest_tny_account_store_get_local_folders_account (self);
1567         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1568                                                                per_account_outbox);
1569         /* Add the pair to the hash table */
1570         g_hash_table_insert (priv->outbox_of_transport,
1571                              transport_account,
1572                              per_account_outbox);
1573         
1574         g_object_unref (local_account);
1575         g_object_unref (per_account_outbox);
1576 }
1577
1578 /*
1579  * This function will be used for both adding new accounts and for the
1580  * initialization. In the initialization we do not want to emit
1581  * signals so notify will be FALSE, in the case of account additions
1582  * we do want to notify the observers
1583  */
1584 static void
1585 insert_account (ModestTnyAccountStore *self,
1586                 const gchar *account,
1587                 gboolean notify)
1588 {
1589         ModestTnyAccountStorePrivate *priv = NULL;
1590         TnyAccount *store_account = NULL, *transport_account = NULL;
1591         
1592         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1593
1594         /* Get the server and the transport account */
1595         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1596         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1597
1598         g_assert (store_account);
1599         g_assert (transport_account);
1600
1601         /* Add accounts to the lists */
1602         tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1603         tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1604         
1605         /* Create a new pseudo-account with an outbox for this
1606            transport account and add it to the global outbox
1607            in the local account */
1608         add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1609         
1610         /* Notify the observers. We do it after everything is
1611            created */
1612         if (notify) {
1613                 TnyAccount *local_account = NULL;
1614                 
1615                 /* Notify the observers about the new server & transport accounts */
1616                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);   
1617                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1618
1619                 /* Notify that the local account changed */
1620                 local_account = modest_tny_account_store_get_local_folders_account (self);
1621                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1622                 g_object_unref (local_account);
1623         }
1624
1625         /* Frees */
1626         g_object_unref (store_account);
1627         g_object_unref (transport_account);
1628 }
1629
1630 static void
1631 on_account_inserted (ModestAccountMgr *acc_mgr, 
1632                      const gchar *account,
1633                      gpointer user_data)
1634 {
1635         /* Insert the account and notify the observers */
1636         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1637 }
1638
1639 static void
1640 on_account_removed (ModestAccountMgr *acc_mgr, const gchar *account,
1641                     gpointer user_data)
1642 {
1643         TnyAccount *store_account = NULL, *transport_account = NULL;
1644         ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
1645         
1646         /* Get the server and the transport account */
1647         store_account = 
1648                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1649         transport_account = 
1650                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1651         
1652         /* If there was any problem creating the account, for example,
1653            with the configuration system this could not exist */
1654         if (store_account) {
1655                 /* Clear the cache */
1656                 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (store_account));
1657
1658                 /* Notify the observers */
1659                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1660                 account_disconnect (store_account); /* disconnect the account */
1661                 g_object_unref (store_account);
1662         } else {
1663                 g_warning ("There is no store account for account %s\n", account);
1664         }
1665
1666         /* If there was any problem creating the account, for example,
1667            with the configuration system this could not exist */
1668         if (transport_account) {
1669                 TnyAccount *local_account = NULL;
1670                 TnyFolder *outbox = NULL;
1671                 ModestTnyAccountStorePrivate *priv = NULL;
1672         
1673                 /* Remove the OUTBOX of the account from the global outbox */
1674                 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1675                 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1676
1677                 if (TNY_IS_FOLDER (outbox)) {
1678                         local_account = modest_tny_account_store_get_local_folders_account (self);
1679                         modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1680                                                                                     outbox);
1681                         g_hash_table_remove (priv->outbox_of_transport, transport_account);
1682
1683                         /* Notify the change in the local account */
1684                         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1685                         g_object_unref (local_account);
1686                 } else {
1687                         g_warning ("Removing a transport account that has no outbox");
1688                 }
1689
1690                 /* Notify the observers */
1691                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1692                 account_disconnect (transport_account); /* disconnect the account */
1693                 g_object_unref (transport_account);
1694         } else {
1695                 g_warning ("There is no transport account for account %s\n", account);
1696         }
1697 }
1698
1699 TnyTransportAccount *
1700 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1701                                                                     const gchar *name)
1702 {
1703         ModestTnyAccountStorePrivate *priv = NULL;
1704         TnyAccount * tny_account = NULL;
1705
1706         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1707
1708         /* Add the account: */
1709         tny_account = 
1710                 modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1711                                                                  priv->session, 
1712                                                                  name,
1713                                                                  get_password,
1714                                                                  forget_password);
1715         if (tny_account) {
1716                 g_object_set_data (G_OBJECT(tny_account), 
1717                                    "account_store", 
1718                                    (gpointer)self);
1719                 
1720                 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1721                 add_outbox_from_transport_account_to_global_outbox (self, 
1722                                                                     name, 
1723                                                                     tny_account);
1724                 
1725         } else
1726                 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1727                             name);
1728
1729         return TNY_TRANSPORT_ACCOUNT (tny_account);
1730 }
1731
1732
1733 static void
1734 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1735 {
1736         ModestTnyAccountStorePrivate *priv = NULL;
1737         GSList *list_specifics = NULL, *iter = NULL;
1738
1739         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1740
1741         ModestConf *conf = modest_runtime_get_conf ();
1742
1743         GError *err = NULL;
1744         list_specifics = modest_conf_get_list (conf,
1745                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1746                                                MODEST_CONF_VALUE_STRING, &err);
1747         if (err) {
1748                 g_printerr ("modest: %s: error getting list: %s\n.", __FUNCTION__, err->message);
1749                 g_error_free (err);
1750                 err = NULL;
1751         }
1752                                 
1753         /* Look at each connection-specific transport account for the 
1754          * modest account: */
1755         iter = list_specifics;
1756         while (iter) {
1757                 /* The list alternates between the connection name and the transport name: */
1758                 iter = g_slist_next (iter);
1759                 if (iter) {
1760                         const gchar* transport_account_name = (const gchar*) (iter->data);
1761                         TnyTransportAccount * account = NULL;
1762                         account = modest_tny_account_store_new_connection_specific_transport_account (
1763                                 self, transport_account_name);
1764                         if (account)
1765                                 g_object_unref (account);
1766                 }                               
1767                 iter = g_slist_next (iter);
1768         }
1769 }
1770
1771 TnyMsg *
1772 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self, 
1773                                                const gchar *uri,
1774                                                TnyAccount **ac_out)
1775 {
1776         TnyIterator *acc_iter;
1777         ModestTnyAccountStorePrivate *priv;
1778         TnyMsg *msg = NULL;
1779         TnyAccount *msg_account = NULL;
1780
1781         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1782         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1783
1784         acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
1785         while (!msg && !tny_iterator_is_done (acc_iter)) {
1786                 TnyList *folders = tny_simple_list_new ();
1787                 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
1788                 TnyIterator *folders_iter = NULL;
1789
1790                 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
1791                 folders_iter = tny_list_create_iterator (folders);
1792
1793                 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
1794                         TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
1795                         msg = tny_folder_find_msg (folder, uri, NULL);
1796
1797                         if (msg)
1798                                 msg_account = g_object_ref (account);
1799
1800                         g_object_unref (folder);
1801                         tny_iterator_next (folders_iter);
1802                 }
1803
1804                 g_object_unref (folders);
1805                 g_object_unref (account);
1806                 tny_iterator_next (acc_iter);
1807         }
1808
1809         g_object_unref (acc_iter);
1810
1811         if (ac_out != NULL)
1812                 *ac_out = msg_account;
1813
1814         return msg;
1815 }
1816
1817 TnyTransportAccount *
1818 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
1819 {
1820         TnyIterator *acc_iter;
1821         ModestTnyAccountStorePrivate *priv;
1822         TnyTransportAccount *header_acc = NULL;
1823         const gchar *msg_id;
1824
1825         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1826         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
1827         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1828
1829         msg_id = modest_tny_send_queue_get_msg_id (header);
1830         acc_iter = tny_list_create_iterator (priv->transport_accounts);
1831         while (!header_acc && !tny_iterator_is_done (acc_iter)) {
1832                 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
1833                 ModestTnySendQueue *send_queue;
1834                 ModestTnySendQueueStatus status;
1835                 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account));
1836                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
1837                 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
1838                         header_acc = g_object_ref(account);
1839                 }
1840                 g_object_unref (account);
1841                 tny_iterator_next (acc_iter);
1842         }
1843
1844         g_object_unref(acc_iter);
1845         return header_acc;
1846 }