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