* Fixes NB#63512, do not show the "Internal message error ...." just ignore it
[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                         const gchar* parent_name =
469                                 modest_tny_account_get_parent_modest_account_name_for_server_account (tny_account);
470                         if (parent_name && strcmp (parent_name, account_name) == 0) {
471                                 found = TRUE;
472                                 modest_tny_account_update_from_account (tny_account, acc_mgr, account_name, type);
473                                 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_CHANGED_SIGNAL], 0, tny_account);
474                         }
475                         g_object_unref (tny_account);
476                 }
477                 tny_iterator_next (iter);
478         }
479
480         if (iter)
481                 g_object_unref (iter);
482         
483         return found;
484 }
485
486
487 static void
488 on_account_changed (ModestAccountMgr *acc_mgr, 
489                     const gchar *account_name, 
490                     gpointer user_data)
491 {
492         ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
493
494         g_debug ("DEBUG: modest: %s\n", __FUNCTION__);
495
496         /* Ignore the change if it's a change in the last_updated value */
497 //      if (key && g_str_has_suffix ((const gchar *) key, MODEST_ACCOUNT_LAST_UPDATED))
498 //              return;
499         
500         if (!update_tny_account_for_account (self, acc_mgr, account_name, TNY_ACCOUNT_TYPE_STORE))
501                 g_warning ("%s: failed to update store account for %s", __FUNCTION__, account_name);
502         if (!update_tny_account_for_account (self, acc_mgr, account_name, TNY_ACCOUNT_TYPE_TRANSPORT))
503                 g_warning ("%s: failed to update transport account for %s", __FUNCTION__, account_name);
504 }
505
506 static void 
507 on_account_settings_hide (GtkWidget *widget, gpointer user_data)
508 {
509         TnyAccount *account = (TnyAccount*)user_data;
510         
511         /* This is easier than using a struct for the user_data: */
512         ModestTnyAccountStore *self = modest_runtime_get_account_store();
513         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
514         
515         const gchar *modest_account_name = 
516                         modest_tny_account_get_parent_modest_account_name_for_server_account (account);
517         if (modest_account_name)
518                 g_hash_table_remove (priv->account_settings_dialog_hash, modest_account_name);
519 }
520
521 static void 
522 show_password_warning_only ()
523 {
524         ModestWindow *main_window = 
525                                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ());
526                                 
527         /* Show an explanatory temporary banner: */
528         hildon_banner_show_information ( 
529                 GTK_WIDGET(main_window), NULL, _("mcen_ib_username_pw_incorrect"));
530 }
531                 
532 static void 
533 show_wrong_password_dialog (TnyAccount *account)
534
535         /* This is easier than using a struct for the user_data: */
536         ModestTnyAccountStore *self = modest_runtime_get_account_store();
537         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
538         
539         const gchar *modest_account_name = 
540                         modest_tny_account_get_parent_modest_account_name_for_server_account (account);
541         if (!modest_account_name) {
542                 g_warning ("%s: modest_tny_account_get_parent_modest_account_name_for_server_account() failed.\n", 
543                         __FUNCTION__);
544         }
545         
546         /* Check whether this window is already open,
547          * for instance because of a previous get_password() call: 
548          */
549         gpointer dialog_as_gpointer = NULL;
550         gboolean found = FALSE;
551         if (priv->account_settings_dialog_hash) {
552                 found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
553                         modest_account_name, NULL, (gpointer*)&dialog_as_gpointer);
554         }
555         ModestAccountSettingsDialog *dialog = dialog_as_gpointer;
556                                         
557         ModestWindow *main_window = 
558                                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ());
559
560         gboolean created_dialog = FALSE;
561         if (!found || !dialog) {
562                 dialog = modest_account_settings_dialog_new ();
563                 modest_account_settings_dialog_set_account_name (dialog, modest_account_name);
564                 modest_account_settings_dialog_switch_to_user_info (dialog);
565                 
566                 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (modest_account_name), dialog);
567                 
568                 created_dialog = TRUE;
569         }
570         
571         /* Show an explanatory temporary banner: */
572         hildon_banner_show_information ( 
573                 GTK_WIDGET(dialog), NULL, _("mcen_ib_username_pw_incorrect"));
574                 
575         if (created_dialog) {
576                 /* Forget it when it closes: */
577                 g_signal_connect_object (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide), 
578                         account, 0);
579                         
580                 /* Show it and delete it when it closes: */
581                 modest_maemo_show_dialog_and_forget (GTK_WINDOW (main_window), GTK_DIALOG (dialog));
582         }
583         else {
584                 /* Just show it instead of showing it and deleting it when it closes,
585                  * though it is probably open already: */
586                 gtk_window_present (GTK_WINDOW (dialog));
587         }
588 }
589
590
591
592 static void
593 request_password_and_wait (ModestTnyAccountStore *account_store, 
594                                          const gchar* server_account_id,
595                                          gchar **username,
596                                          gchar **password,
597                                          gboolean *cancel, 
598                                          gboolean *remember)
599 {
600                         g_signal_emit (G_OBJECT(account_store), signals[PASSWORD_REQUESTED_SIGNAL], 0,
601                                server_account_id, /* server_account_name */
602                                username, password, cancel, remember);
603 }
604
605 /* This callback will be called by Tinymail when it needs the password
606  * from the user or the account settings.
607  * It can also call forget_password() before calling this,
608  * so that we clear wrong passwords out of our account settings.
609  * Note that TnyAccount here will be the server account. */
610 static gchar*
611 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
612 {
613         /* TODO: Settting cancel to FALSE does not actually cancel everything.
614          * We still get multiple requests afterwards, so we end up showing the 
615          * same dialogs repeatedly.
616          */
617          
618         printf ("DEBUG: modest: %s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
619           
620         g_return_val_if_fail (account, NULL);
621           
622         const TnyAccountStore *account_store = NULL;
623         ModestTnyAccountStore *self = NULL;
624         ModestTnyAccountStorePrivate *priv;
625         gchar *username = NULL;
626         gchar *pwd = NULL;
627         gpointer pwd_ptr = NULL;
628         gboolean already_asked = FALSE;
629
630         /* Initialize the output parameter: */
631         if (cancel)
632                 *cancel = FALSE;
633                 
634         const gchar *server_account_name = tny_account_get_id (account);
635         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
636                                                              "account_store"));
637
638         if (!server_account_name || !account_store) {
639                 g_warning ("modest: %s: could not retrieve account_store for account %s",
640                            __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
641                 if (cancel)
642                         *cancel = TRUE;
643                 
644                 return NULL;
645         }
646
647         self = MODEST_TNY_ACCOUNT_STORE (account_store);
648         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
649         
650         /* This hash map stores passwords, including passwords that are not stored in gconf. */
651         /* Is it in the hash? if it's already there, it must be wrong... */
652         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
653                                    * type-punned ptrs...*/
654         already_asked = priv->password_hash && 
655                                 g_hash_table_lookup_extended (priv->password_hash,
656                                                       server_account_name,
657                                                       NULL,
658                                                       (gpointer*)&pwd_ptr);
659                                                       
660         printf ("DEBUG: modest: %s: Already asked = %d\n", __FUNCTION__, already_asked);
661
662         /* If the password is not already there, try ModestConf */
663         if (!already_asked) {
664                 pwd  = modest_server_account_get_password (priv->account_mgr,
665                                                       server_account_name);
666                 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
667         }
668
669         /* If it was already asked, it must have been wrong, so ask again */
670         if (already_asked || !pwd || strlen(pwd) == 0) {
671                 /* As per the UI spec, if no password was set in the account settings, 
672                  * ask for it now. But if the password is wrong in the account settings, 
673                  * then show a banner and the account settings dialog so it can be corrected:
674                  */
675                 const gboolean settings_have_password = 
676                         modest_server_account_get_has_password (priv->account_mgr, server_account_name);
677                 printf ("DEBUG: modest: %s: settings_have_password=%d\n", __FUNCTION__, settings_have_password);
678                 if (settings_have_password) {
679                         /* The password must be wrong, so show the account settings dialog so it can be corrected: */
680                         show_wrong_password_dialog (account);
681                         
682                         if (cancel)
683                                 *cancel = TRUE;
684                                 
685                         return NULL;
686                 }
687         
688                 /* we don't have it yet. Get the password from the user */
689                 const gchar* account_id = tny_account_get_id (account);
690                 gboolean remember = FALSE;
691                 pwd = NULL;
692                 
693                 if (already_asked) {
694                         /* Show an info banner, before we show the protected password dialog: */
695                         show_password_warning_only();
696                 }
697                 
698                 request_password_and_wait (self, account_id, 
699                                &username, &pwd, cancel, &remember);
700                 
701                 if (!*cancel) {
702                         /* The password will be returned as the result,
703                          * but we need to tell tinymail about the username too: */
704                         tny_account_set_user (account, username);
705                         
706                         /* Do not save the password in gconf, 
707                          * because the UI spec says "The password will never be saved in the account": */
708                         /*
709                         if (remember) {
710                                 printf ("%s: Storing username=%s, password=%s\n", 
711                                         __FUNCTION__, username, pwd);
712                                 modest_server_account_set_username (priv->account_mgr, server_account_name,
713                                                                username);
714                                 modest_server_account_set_password (priv->account_mgr, server_account_name,
715                                                                pwd);
716                         }
717                         */
718
719                         /* We need to dup the string even knowing that
720                            it's already a dup of the contents of an
721                            entry, because it if it's wrong, then camel
722                            will free it */
723                         g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
724                 } else {
725                         g_hash_table_remove (priv->password_hash, server_account_name);
726                         
727                         g_free (pwd);
728                         pwd = NULL;
729                 }
730
731                 g_free (username);
732                 username = NULL;
733         } else
734                 *cancel = FALSE;
735  
736     /* printf("  DEBUG: %s: returning %s\n", __FUNCTION__, pwd); */
737         
738         return pwd;
739 }
740
741 /* tinymail calls this if the connection failed due to an incorrect password.
742  * And it seems to call this for any general connection failure. */
743 static void
744 forget_password (TnyAccount *account)
745 {
746         printf ("DEBUG: %s\n", __FUNCTION__);
747         ModestTnyAccountStore *self;
748         ModestTnyAccountStorePrivate *priv;
749         const TnyAccountStore *account_store;
750         gchar *pwd;
751         const gchar *key;
752         
753         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
754                                                              "account_store"));
755         self = MODEST_TNY_ACCOUNT_STORE (account_store);
756         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
757         key  = tny_account_get_id (account);
758
759         /* Do not remove the key, this will allow us to detect that we
760            have already asked for it at least once */
761         pwd = g_hash_table_lookup (priv->password_hash, key);
762         if (pwd) {
763                 memset (pwd, 0, strlen (pwd));
764                 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
765         }
766
767         /* Remove from configuration system */
768         /*
769         modest_account_mgr_unset (priv->account_mgr,
770                                   key, MODEST_ACCOUNT_PASSWORD, TRUE);
771         */
772 }
773
774 static void
775 modest_tny_account_store_finalize (GObject *obj)
776 {
777         GnomeVFSVolumeMonitor *volume_monitor;
778         ModestTnyAccountStore *self        = MODEST_TNY_ACCOUNT_STORE(obj);
779         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
780
781         g_free (priv->cache_dir);
782         priv->cache_dir = NULL;
783         
784         if (priv->password_hash) {
785                 g_hash_table_destroy (priv->password_hash);
786                 priv->password_hash = NULL;
787         }
788
789         if (priv->account_settings_dialog_hash) {
790                 g_hash_table_destroy (priv->account_settings_dialog_hash);
791                 priv->account_settings_dialog_hash = NULL;
792         }
793
794         if (priv->outbox_of_transport) {
795                 g_hash_table_destroy (priv->outbox_of_transport);
796                 priv->outbox_of_transport = NULL;
797         }
798
799
800         /* Disconnect VFS signals */
801         volume_monitor = gnome_vfs_get_volume_monitor ();
802         if (g_signal_handler_is_connected (volume_monitor, 
803                                            priv->volume_mounted_handler))
804                 g_signal_handler_disconnect (volume_monitor, 
805                                              priv->volume_mounted_handler);
806         if (g_signal_handler_is_connected (volume_monitor, 
807                                            priv->volume_unmounted_handler))
808                 g_signal_handler_disconnect (volume_monitor, 
809                                              priv->volume_unmounted_handler);
810
811         if (priv->account_mgr) {
812                 /* Disconnect signals */
813                 if (g_signal_handler_is_connected (priv->account_mgr, 
814                                                    priv->acc_inserted_handler))
815                         g_signal_handler_disconnect (priv->account_mgr, 
816                                                      priv->acc_inserted_handler);
817                 if (g_signal_handler_is_connected (priv->account_mgr, 
818                                                    priv->acc_changed_handler))
819                         g_signal_handler_disconnect (priv->account_mgr, 
820                                                      priv->acc_changed_handler);
821                 if (g_signal_handler_is_connected (priv->account_mgr, 
822                                                    priv->acc_removed_handler))
823                         g_signal_handler_disconnect (priv->account_mgr, 
824                                                      priv->acc_removed_handler);
825
826                 g_object_unref (G_OBJECT(priv->account_mgr));
827                 priv->account_mgr = NULL;
828         }
829
830         if (priv->device) {
831                 g_object_unref (G_OBJECT(priv->device));
832                 priv->device = NULL;
833         }
834
835         /* Destroy all accounts. Disconnect all accounts before they are destroyed */
836         if (priv->store_accounts) {
837                 tny_list_foreach (priv->store_accounts, foreach_account_disconnect, NULL);
838                 g_object_unref (priv->store_accounts);
839                 priv->store_accounts = NULL;
840         }
841
842         if (priv->transport_accounts) {
843                 tny_list_foreach (priv->transport_accounts, foreach_account_disconnect, NULL);
844                 g_object_unref (priv->transport_accounts);
845                 priv->transport_accounts = NULL;
846         }
847
848         if (priv->store_accounts_outboxes) {
849                 g_object_unref (priv->store_accounts_outboxes);
850                 priv->store_accounts_outboxes = NULL;
851         }
852                 
853         if (priv->session) {
854                 camel_object_unref (CAMEL_OBJECT(priv->session));
855                 priv->session = NULL;
856         }
857         
858         G_OBJECT_CLASS(parent_class)->finalize (obj);
859 }
860
861 gboolean 
862 volume_path_is_mounted (const gchar* path)
863 {
864         g_return_val_if_fail (path, FALSE);
865
866         gboolean result = FALSE;
867         gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
868         g_return_val_if_fail (path_as_uri, FALSE);
869
870         /* Get the monitor singleton: */
871         GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
872
873         /* This seems like a simpler way to do this, but it returns a   
874          * GnomeVFSVolume even if the drive is not mounted: */
875         /*
876         GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor, 
877                 MODEST_MCC1_VOLUMEPATH);
878         if (volume) {
879                 gnome_vfs_volume_unref(volume);
880         }
881         */
882
883         /* Get the mounted volumes from the monitor: */
884         GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
885         GList *iter = list;
886         for (iter = list; iter; iter = g_list_next (iter)) {
887                 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
888                 if (volume) {
889                         /*
890                         char *display_name = 
891                                 gnome_vfs_volume_get_display_name (volume);
892                         printf ("volume display name=%s\n", display_name);
893                         g_free (display_name);
894                         */
895                         
896                         char *uri = 
897                                 gnome_vfs_volume_get_activation_uri (volume);
898                         /* printf ("  uri=%s\n", uri); */
899                         if (uri && (strcmp (uri, path_as_uri) == 0))
900                                 result = TRUE;
901
902                         g_free (uri);
903
904                         gnome_vfs_volume_unref (volume);
905                 }
906         }
907
908         g_list_free (list);
909
910         g_free (path_as_uri);
911
912         return result;
913 }
914
915 ModestTnyAccountStore*
916 modest_tny_account_store_new (ModestAccountMgr *account_mgr, 
917                               TnyDevice *device) 
918 {
919         GObject *obj;
920         ModestTnyAccountStorePrivate *priv;
921         TnyAccount *local_account = NULL;
922         
923         g_return_val_if_fail (account_mgr, NULL);
924         g_return_val_if_fail (device, NULL);
925
926         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
927         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
928
929         priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
930         priv->device = g_object_ref (device);
931         
932         priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
933         if (!priv->session) {
934                 g_warning ("failed to get TnySessionCamel");
935                 return NULL;
936         }
937
938         /* Set the ui locker */ 
939         tny_session_camel_set_ui_locker (priv->session,  tny_gtk_lockable_new ());
940                 
941         /* Connect signals */
942         priv->acc_inserted_handler = g_signal_connect (G_OBJECT(account_mgr), "account_inserted",
943                                                       G_CALLBACK (on_account_inserted), obj);
944         priv->acc_changed_handler = g_signal_connect (G_OBJECT(account_mgr), "account_changed",
945                                                       G_CALLBACK (on_account_changed), obj);
946         priv->acc_removed_handler = g_signal_connect (G_OBJECT(account_mgr), "account_removed",
947                                                       G_CALLBACK (on_account_removed), obj);
948
949         /* Create the lists of accounts */
950         priv->store_accounts = tny_simple_list_new ();
951         priv->transport_accounts = tny_simple_list_new ();
952         priv->store_accounts_outboxes = tny_simple_list_new ();
953
954         /* Create the local folders account */
955         local_account = 
956                 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
957         tny_list_append (priv->store_accounts, G_OBJECT(local_account));
958         g_object_unref (local_account); 
959
960         /* Add the other remote accounts. Do this after adding the
961            local account, because we need to add our outboxes to the
962            global OUTBOX hosted in the local account */
963         add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
964         
965         /* FIXME: I'm doing this (adding an "if (FALSE)"because this
966            stuff is not working properly and could cause SIGSEVs, for
967            example one send queue will be created for each connection
968            specific SMTP server, so when tinymail asks for the outbox
969            it will return NULL because there is no outbox folder for
970            this specific transport accounts, and it's a must that the
971            send queue returns an outbox */
972         if (FALSE)
973                 /* Add connection-specific transport accounts */
974                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
975         
976         /* This is a singleton, so it does not need to be unrefed. */
977         if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
978                 /* It is mounted: */
979                 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */); 
980         }
981         
982         return MODEST_TNY_ACCOUNT_STORE(obj);
983 }
984
985 static void
986 modest_tny_account_store_get_accounts  (TnyAccountStore *self, 
987                                         TnyList *list,
988                                         TnyGetAccountsRequestType request_type)
989 {
990         ModestTnyAccountStorePrivate *priv;
991         
992         g_return_if_fail (self);
993         g_return_if_fail (TNY_IS_LIST(list));
994         
995         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
996         
997         switch (request_type) {
998         case TNY_ACCOUNT_STORE_BOTH:
999                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1000                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1001                 break;
1002         case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
1003                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1004                 break;
1005         case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
1006                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1007                 break;
1008         default:
1009                 g_return_if_reached ();
1010         }
1011
1012         /* Initialize session. Why do we need this ??? */
1013         tny_session_camel_set_initialized (priv->session);
1014 }
1015
1016
1017 static const gchar*
1018 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
1019 {
1020         ModestTnyAccountStorePrivate *priv;
1021         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1022         
1023         if (!priv->cache_dir)
1024                 priv->cache_dir = g_build_filename (g_get_home_dir(), 
1025                                                     MODEST_DIR, MODEST_CACHE_DIR, NULL);
1026         return priv->cache_dir;
1027 }
1028
1029
1030 /*
1031  * callers need to unref
1032  */
1033 static TnyDevice*
1034 modest_tny_account_store_get_device (TnyAccountStore *self)
1035 {
1036         ModestTnyAccountStorePrivate *priv;
1037
1038         g_return_val_if_fail (self, NULL);
1039         
1040         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1041
1042         if (priv->device)
1043                 return g_object_ref (G_OBJECT(priv->device));
1044         else
1045                 return NULL;
1046 }
1047
1048
1049 static TnyAccount*
1050 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1051 {
1052         return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self), 
1053                                                             MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1054                                                             url_string);
1055 }
1056
1057
1058
1059 static gboolean
1060 modest_tny_account_store_alert (TnyAccountStore *self, 
1061                                 TnyAccount *account, 
1062                                 TnyAlertType type,
1063                                 gboolean question, 
1064                                 const GError *error)
1065 {
1066         ModestTransportStoreProtocol proto =
1067                 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN; 
1068         const gchar* server_name = NULL;
1069         gchar *prompt = NULL;
1070         gboolean retval;
1071
1072
1073         g_return_val_if_fail (account, FALSE);
1074         g_return_val_if_fail (error, FALSE);
1075         
1076         if ((error->domain != TNY_ACCOUNT_ERROR) && (error->domain != TNY_ACCOUNT_STORE_ERROR))
1077                 return FALSE;
1078         
1079         /* Get the server name: */
1080         server_name = tny_account_get_hostname (account);
1081
1082         if (account) {
1083                 const gchar *proto_name = tny_account_get_proto (account);
1084                 if (proto_name)
1085                         proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1086                 else {
1087                         g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__, 
1088                                   tny_account_get_id (account));
1089                         return FALSE;
1090                 }
1091         }
1092
1093         switch (error->code) {
1094         case TNY_ACCOUNT_STORE_ERROR_CANCEL_ALERT:
1095         case TNY_ACCOUNT_ERROR_TRY_CONNECT_USER_CANCEL:
1096                 /* Don't show waste the user's time by showing him a dialog telling 
1097                  * him that he has just cancelled something: */
1098                 return TRUE;
1099
1100         case TNY_ACCOUNT_ERROR_TRY_CONNECT_HOST_LOOKUP_FAILED:
1101         case TNY_ACCOUNT_ERROR_TRY_CONNECT_SERVICE_UNAVAILABLE:
1102                 /* TODO: Show the appropriate message, depending on whether it's POP or IMAP: */                
1103                 switch (proto) {
1104                 case MODEST_PROTOCOL_STORE_POP:
1105                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1106                                                   server_name);
1107                         break;
1108                 case MODEST_PROTOCOL_STORE_IMAP:
1109                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1110                                                   server_name);
1111                         break;
1112                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1113                         prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1114                                                   server_name);
1115                         break;
1116                 default:
1117                         g_return_val_if_reached (FALSE);
1118                 }
1119                 break;
1120                 
1121         case TNY_ACCOUNT_ERROR_TRY_CONNECT_AUTHENTICATION_NOT_SUPPORTED:
1122                 /* This is "Secure connection failed", even though the logical
1123                  * ID has _certificate_ in the name: */
1124                 prompt = g_strdup (_("mail_ni_ssl_certificate_error")); 
1125                 break;
1126                         
1127         case TNY_ACCOUNT_ERROR_TRY_CONNECT_CERTIFICATE:
1128                 /* We'll show the proper dialog later */
1129                 break;
1130                 
1131         case TNY_ACCOUNT_ERROR_TRY_CONNECT:
1132                 /* The tinymail camel implementation just sends us this for almost 
1133                  * everything, so we have to guess at the cause.
1134                  * It could be a wrong password, or inability to resolve a hostname, 
1135                  * or lack of network, or incorrect authentication method, or something entirely different: */
1136                 /* TODO: Fix camel to provide specific error codes, and then use the 
1137                  * specific dialog messages from Chapter 12 of the UI spec.
1138                  */
1139         case TNY_ACCOUNT_STORE_ERROR_UNKNOWN_ALERT: 
1140                 return FALSE;                   
1141         default:
1142                 g_return_val_if_reached (FALSE);
1143         }
1144         
1145
1146         if (error->code == TNY_ACCOUNT_ERROR_TRY_CONNECT_CERTIFICATE)
1147                 retval = modest_platform_run_certificate_conformation_dialog (server_name,
1148                                                                               error->message);
1149         else
1150                 retval = modest_platform_run_alert_dialog (prompt, question);
1151         
1152         if (prompt)
1153                 g_free (prompt);
1154         
1155         return retval;
1156 }
1157
1158
1159 static void
1160 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1161 {
1162         TnyAccountStoreIface *klass;
1163
1164         g_return_if_fail (g);
1165
1166         klass = (TnyAccountStoreIface *)g;
1167
1168         klass->get_accounts_func =
1169                 modest_tny_account_store_get_accounts;
1170         klass->get_cache_dir_func =
1171                 modest_tny_account_store_get_cache_dir;
1172         klass->get_device_func =
1173                 modest_tny_account_store_get_device;
1174         klass->alert_func =
1175                 modest_tny_account_store_alert;
1176         klass->find_account_func =
1177                 modest_tny_account_store_find_account_by_url;
1178 }
1179
1180 void
1181 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1182                                             ModestTnyGetPassFunc func)
1183 {
1184         /* not implemented, we use signals */
1185         g_printerr ("modest: set_get_pass_func not implemented\n");
1186 }
1187
1188 TnySessionCamel*
1189 modest_tny_account_store_get_session  (TnyAccountStore *self)
1190 {
1191         g_return_val_if_fail (self, NULL);
1192         return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1193 }
1194
1195 static TnyAccount*
1196 get_tny_account_by (TnyList *accounts,
1197                     ModestTnyAccountStoreQueryType type,
1198                     const gchar *str)
1199 {
1200         TnyIterator *iter = NULL;
1201         gboolean found = FALSE;
1202         TnyAccount *retval = NULL;
1203
1204         iter = tny_list_create_iterator (accounts);
1205         while (!tny_iterator_is_done (iter) && !found) {
1206                 TnyAccount *tmp_account = NULL;
1207                 const gchar *val = NULL;
1208
1209                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1210                 switch (type) {
1211                 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1212                         val = tny_account_get_id (tmp_account);
1213                         break;
1214                 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1215                         val = tny_account_get_url_string (tmp_account);
1216                         break;
1217                 }
1218                 
1219                 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL && 
1220                     tny_account_matches_url_string (tmp_account, str)) {
1221                         retval = g_object_ref (tmp_account);
1222                         found = TRUE;
1223                 } else {
1224                         if (strcmp (val, str) == 0) {
1225                                 retval = g_object_ref (tmp_account);
1226                                 found = TRUE;
1227                         }
1228                 }
1229                 g_object_unref (tmp_account);
1230                 tny_iterator_next (iter);
1231         }
1232         g_object_unref (iter);
1233
1234         return retval;
1235 }
1236
1237 TnyAccount*
1238 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self, 
1239                                              ModestTnyAccountStoreQueryType type,
1240                                              const gchar *str)
1241 {
1242         TnyAccount *account = NULL;
1243         ModestTnyAccountStorePrivate *priv;     
1244         
1245         g_return_val_if_fail (self, NULL);
1246         g_return_val_if_fail (str, NULL);
1247         
1248         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1249         
1250         /* Search in store accounts */
1251         account = get_tny_account_by (priv->store_accounts, type, str);
1252
1253         /* If we already found something, no need to search the transport accounts */
1254         if (!account) {
1255                 account = get_tny_account_by (priv->transport_accounts, type, str);
1256
1257                 /* If we already found something, no need to search the
1258                    per-account outbox accounts */
1259                 if (!account)
1260                         account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1261         }
1262
1263         /* Warn if nothing was found. This is generally unusual. */
1264         if (!account) {
1265                 g_warning("%s: Failed to find account with %s=%s\n", 
1266                           __FUNCTION__, 
1267                           (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",                     
1268                           str);
1269         }
1270
1271         /* Returns a new reference to the account if found */   
1272         return account;
1273 }
1274
1275 TnyAccount*
1276 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1277                                              const gchar *account_name,
1278                                              TnyAccountType type)
1279 {
1280         ModestTnyAccountStorePrivate *priv = NULL;
1281         TnyAccount *retval = NULL;
1282         TnyList *account_list = NULL;
1283         TnyIterator *iter = NULL;
1284         gboolean found;
1285
1286         g_return_val_if_fail (self, NULL);
1287         g_return_val_if_fail (account_name, NULL);
1288         g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || 
1289                               type == TNY_ACCOUNT_TYPE_TRANSPORT,
1290                               NULL);
1291         
1292         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1293
1294         account_list = (type == TNY_ACCOUNT_TYPE_STORE) ? 
1295                 priv->store_accounts : 
1296                 priv->transport_accounts;
1297
1298         if (!account_list) {
1299                 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__, 
1300                         (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1301                 return NULL;
1302         }
1303         
1304         /* Look for the server account */
1305         found = FALSE;
1306         iter = tny_list_create_iterator (account_list);
1307         while (!tny_iterator_is_done (iter) && !found) {
1308                 const gchar *modest_acc_name;
1309                 TnyAccount *tmp_account;
1310
1311                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1312                 modest_acc_name = 
1313                         modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1314                 
1315                 if (!strcmp (account_name, modest_acc_name)) {
1316                         found = TRUE;
1317                         retval = g_object_ref (tmp_account);
1318                 }
1319                 /* Free and continue */
1320                 g_object_unref (tmp_account);
1321                 tny_iterator_next (iter);
1322         }
1323
1324         if (!found) {
1325                 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1326                             "Number of server accounts of this type=%d\n", __FUNCTION__,
1327                             (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1328                             account_name, tny_list_get_length (account_list));
1329         }
1330
1331         /* Returns a new reference */
1332         return retval;
1333 }
1334
1335 static TnyAccount*
1336 get_smtp_specific_transport_account_for_open_connection (ModestTnyAccountStore *self,
1337         const gchar *account_name)
1338 {
1339         /* Get the current connection: */
1340         TnyDevice *device = modest_runtime_get_device ();
1341         
1342         if (!tny_device_is_online (device))
1343                 return NULL;
1344
1345         g_return_val_if_fail (self, NULL);
1346         
1347         
1348 #ifdef MODEST_PLATFORM_MAEMO
1349         g_assert (TNY_IS_MAEMO_CONIC_DEVICE (device));
1350         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1351         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1352         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1353         if (!iap_id)
1354                 return NULL;
1355                 
1356         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1357         if (!connection)
1358                 return NULL;
1359                 
1360         const gchar *connection_name = con_ic_iap_get_name (connection);
1361         /* printf ("DEBUG: %s: connection_name=%s\n", __FUNCTION__, connection_name); */
1362         if (!connection_name)
1363                 return NULL;
1364         
1365         /*  Get the connection-specific transport acccount, if any: */
1366         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1367
1368         /* Check if this account has connection-specific SMTP enabled */
1369         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1370                 return NULL;
1371         }
1372
1373         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1374                 connection_name);
1375
1376         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1377         if (!server_account_name) {
1378                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1379         }
1380                 
1381         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1382                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1383                                                                            server_account_name);
1384
1385         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1386         g_free (server_account_name);   
1387
1388         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1389         g_object_unref (connection);
1390         
1391         return account;
1392 #else
1393         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1394 #endif /* MODEST_PLATFORM_MAEMO */
1395 }
1396
1397                                                                  
1398 TnyAccount*
1399 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1400                                                                     const gchar *account_name)
1401 {
1402         g_return_val_if_fail (self, NULL);
1403         g_return_val_if_fail (account_name, NULL);
1404
1405         if (!account_name || !self)
1406                 return NULL;
1407         
1408         /*  Get the connection-specific transport acccount, if any: */
1409         /* Note: This gives us a reference: */
1410         TnyAccount *account =
1411                 get_smtp_specific_transport_account_for_open_connection (self, account_name);
1412                         
1413         /* If there is no connection-specific transport account (the common case), 
1414          * just get the regular transport account: */
1415         if (!account) {
1416                 /* The special local folders don't have transport accounts. */
1417                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1418                         account = NULL;
1419                 else {
1420                         /* Note: This gives us a reference: */
1421                         account = modest_tny_account_store_get_server_account (self, account_name, 
1422                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1423                 }
1424         }
1425                         
1426         /* returns a reference. */     
1427         return account;
1428 }
1429
1430 TnyAccount*
1431 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1432 {
1433         TnyAccount *account = NULL;
1434         ModestTnyAccountStorePrivate *priv;
1435         TnyIterator *iter;
1436         gboolean found;
1437
1438         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1439         
1440         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1441
1442         found = FALSE;
1443         iter = tny_list_create_iterator (priv->store_accounts);
1444         while (!tny_iterator_is_done (iter) && !found) {
1445                 TnyAccount *tmp_account;
1446
1447                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1448                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1449                         account = g_object_ref (tmp_account);
1450                         found = TRUE;
1451                 }
1452                 g_object_unref (tmp_account);
1453                 tny_iterator_next (iter);
1454         }
1455         g_object_unref (iter);
1456
1457         /* Returns a new reference to the account */
1458         return account;
1459 }
1460
1461 /*********************************************************************************/
1462 static void
1463 add_existing_accounts (ModestTnyAccountStore *self)
1464 {
1465         GSList *account_names = NULL, *iter = NULL;
1466         ModestTnyAccountStorePrivate *priv = NULL;
1467         
1468         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1469
1470         /* These are account names, not server_account names */
1471         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1472
1473         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1474                 const gchar *account_name = (const gchar*) iter->data;
1475                 
1476                 /* Insert all enabled accounts without notifying */
1477                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1478                         insert_account (self, account_name, FALSE);
1479         }
1480         modest_account_mgr_free_account_names (account_names);
1481 }
1482
1483 static TnyAccount*
1484 create_tny_account (ModestTnyAccountStore *self,
1485                     const gchar *name,
1486                     TnyAccountType type)
1487 {
1488         TnyAccount *account = NULL;
1489         ModestTnyAccountStorePrivate *priv = NULL;
1490         
1491         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1492
1493         account = modest_tny_account_new_from_account (priv->account_mgr,
1494                                                        name, type, 
1495                                                        priv->session,
1496                                                        get_password,
1497                                                        forget_password);
1498
1499         if (account) {
1500                 /* Forget any cached password for the account, so that
1501                    we use a new account if any */
1502                 modest_tny_account_store_forget_password_in_memory (self, 
1503                                                                     tny_account_get_id (account));
1504                 /* Set the account store */                             
1505                 g_object_set_data (G_OBJECT(account), "account_store", self);
1506         } else {
1507                 g_printerr ("modest: failed to create account for %s\n", name);
1508         }
1509
1510         return account;
1511 }
1512
1513
1514 static void
1515 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1516                                                     const gchar *account_name,
1517                                                     TnyAccount *transport_account)
1518 {
1519         TnyList *folders = NULL;
1520         TnyIterator *iter_folders = NULL;
1521         TnyAccount *local_account = NULL, *account_outbox = NULL;
1522         TnyFolder *per_account_outbox = NULL;
1523         ModestTnyAccountStorePrivate *priv = NULL;
1524
1525         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1526
1527         /* Create per account local outbox */
1528         account_outbox = 
1529                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1530                                                                             account_name, 
1531                                                                             priv->session);
1532         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1533
1534         /* Get the outbox folder */
1535         folders = tny_simple_list_new ();
1536         tny_folder_store_get_folders (TNY_FOLDER_STORE (account_outbox), folders, NULL, NULL);
1537         g_assert (tny_list_get_length (folders) == 1);
1538                 
1539         iter_folders = tny_list_create_iterator (folders);
1540         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1541         g_object_unref (iter_folders);
1542         g_object_unref (folders);
1543         g_object_unref (account_outbox);
1544
1545         /* Add the outbox of the new per-account-local-outbox account
1546            to the global local merged OUTBOX of the local folders
1547            account */
1548         local_account = modest_tny_account_store_get_local_folders_account (self);
1549         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1550                                                                per_account_outbox);
1551         /* Add the pair to the hash table */
1552         g_hash_table_insert (priv->outbox_of_transport,
1553                              transport_account,
1554                              per_account_outbox);
1555         
1556         g_object_unref (local_account);
1557         g_object_unref (per_account_outbox);
1558 }
1559
1560
1561 /*
1562  * This function will be used for both adding new accounts and for the
1563  * initialization. In the initialization we do not want to emit
1564  * signals so notify will be FALSE, in the case of account additions
1565  * we do want to notify the observers
1566  */
1567 static void
1568 insert_account (ModestTnyAccountStore *self,
1569                 const gchar *account,
1570                 gboolean notify)
1571 {
1572         ModestTnyAccountStorePrivate *priv = NULL;
1573         TnyAccount *store_account = NULL, *transport_account = NULL;
1574         
1575         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1576
1577         /* Get the server and the transport account */
1578         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1579         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1580
1581         /* Add to the list, and notify the observers */
1582         if (store_account) {
1583                 tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1584                 if (notify)
1585                         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1586                 g_object_unref (store_account);
1587         }
1588
1589         /* Add to the list, and notify the observers */
1590         if (transport_account) {
1591                 /* Add account to the list */
1592                 tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1593                 g_assert (TNY_IS_ACCOUNT (transport_account));
1594
1595                 /* Create a new pseudo-account with an outbox for this
1596                    transport account and add it to the global outbox
1597                    in the local account */
1598                 add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1599
1600                 if (notify) {
1601                         TnyAccount *local_account = NULL;
1602
1603                         /* Notify that the local account changed */
1604                         local_account = modest_tny_account_store_get_local_folders_account (self);
1605                         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1606                         g_object_unref (local_account);
1607                         
1608                         /* Notify the observers about the new account */
1609                         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1610                 }
1611
1612                 g_object_unref (transport_account);
1613         }
1614 }
1615
1616 static void
1617 on_account_inserted (ModestAccountMgr *acc_mgr, 
1618                      const gchar *account,
1619                      gpointer user_data)
1620 {
1621         /* Insert the account and notify the observers */
1622         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1623 }
1624
1625 static void
1626 on_account_removed (ModestAccountMgr *acc_mgr, 
1627                     const gchar *account,
1628                     gpointer user_data)
1629 {
1630         TnyAccount *store_account = NULL, *transport_account = NULL;
1631         ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
1632         
1633         /* Get the server and the transport account */
1634         store_account = 
1635                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1636         transport_account = 
1637                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1638
1639         /* If there was any problem creating the account, for example,
1640            with the configuration system this could not exist */
1641         if (store_account) {
1642                 /* Clear the cache */
1643                 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (store_account));
1644
1645                 /* Notify the observers */
1646                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1647                 g_object_unref (store_account);
1648         } else {
1649                 g_warning ("There is no store account for account %s\n", account);
1650         }
1651
1652         /* If there was any problem creating the account, for example,
1653            with the configuration system this could not exist */
1654         if (transport_account) {
1655                 TnyAccount *local_account = NULL;
1656                 TnyFolder *outbox = NULL;
1657                 ModestTnyAccountStorePrivate *priv = NULL;
1658         
1659                 /* Remove the OUTBOX of the account from the global outbox */
1660                 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1661                 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1662
1663                 if (TNY_IS_FOLDER (outbox)) {
1664                         local_account = modest_tny_account_store_get_local_folders_account (self);
1665                         modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1666                                                                                     outbox);
1667                         g_hash_table_remove (priv->outbox_of_transport, transport_account);
1668
1669                         /* Notify the change in the local account */
1670                         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1671                         g_object_unref (local_account);
1672                 } else {
1673                         g_warning ("Removing a transport account that has no outbox");
1674                 }
1675
1676                 /* Notify the observers */
1677                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1678                 g_object_unref (transport_account);
1679         } else {
1680                 g_warning ("There is no transport account for account %s\n", account);
1681         }
1682 }
1683
1684 static void
1685 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1686 {
1687         ModestTnyAccountStorePrivate *priv = NULL;
1688         GSList *list_specifics = NULL, *iter = NULL;
1689
1690         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1691
1692         ModestConf *conf = modest_runtime_get_conf ();
1693
1694         GError *err = NULL;
1695         list_specifics = modest_conf_get_list (conf,
1696                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1697                                                MODEST_CONF_VALUE_STRING, &err);
1698         if (err) {
1699                 g_printerr ("modest: %s: error getting list: %s\n.", __FUNCTION__, err->message);
1700                 g_error_free (err);
1701                 err = NULL;
1702         }
1703                                 
1704         /* Look at each connection-specific transport account for the 
1705          * modest account: */
1706         iter = list_specifics;
1707         while (iter) {
1708                 /* The list alternates between the connection name and the transport name: */
1709                 iter = g_slist_next (iter);
1710                 if (iter) {
1711                         const gchar* transport_account_name = (const gchar*) (iter->data);
1712                         if (transport_account_name) {
1713                                 TnyAccount * tny_account = NULL;
1714                                 /* Add the account: */
1715                                 tny_account = 
1716                                         modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1717                                                                                          priv->session, 
1718                                                                                          transport_account_name);
1719                                 if (tny_account) {
1720                                         g_object_set_data (G_OBJECT(tny_account), 
1721                                                            "account_store", 
1722                                                            (gpointer)self);
1723
1724                                         tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1725                                         g_object_unref (tny_account);
1726                                 } else
1727                                         g_printerr ("modest: failed to create smtp-specific account for %s\n",
1728                                                     transport_account_name);
1729                         }
1730                 }                               
1731                 iter = g_slist_next (iter);
1732         }
1733 }