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