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