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