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