* Select the first inbox of the newly selected account
[modest] / src / modest-account-mgr.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 <modest-marshal.h>
32 #include <modest-runtime.h>
33 #include <modest-account-mgr.h>
34 #include <modest-account-mgr-priv.h>
35 #include <modest-account-mgr-helpers.h>
36 #include <modest-platform.h>
37
38 /* 'private'/'protected' functions */
39 static void modest_account_mgr_class_init (ModestAccountMgrClass * klass);
40 static void modest_account_mgr_init       (ModestAccountMgr * obj);
41 static void modest_account_mgr_finalize   (GObject * obj);
42 static void modest_account_mgr_base_init  (gpointer g_class);
43
44 static const gchar *_modest_account_mgr_get_account_keyname_cached (ModestAccountMgrPrivate *priv, 
45                                                                     const gchar* account_name,
46                                                                     const gchar *name, 
47                                                                     gboolean is_server);
48
49 static gboolean modest_account_mgr_unset_default_account (ModestAccountMgr *self);
50
51 /* list my signals */
52 enum {
53         ACCOUNT_INSERTED_SIGNAL,
54         ACCOUNT_CHANGED_SIGNAL,
55         ACCOUNT_REMOVED_SIGNAL,
56         ACCOUNT_BUSY_SIGNAL,
57         DEFAULT_ACCOUNT_CHANGED_SIGNAL,
58         DISPLAY_NAME_CHANGED_SIGNAL,
59         ACCOUNT_UPDATED_SIGNAL,
60         LAST_SIGNAL
61 };
62
63 /* globals */
64 static GObjectClass *parent_class = NULL;
65 static guint signals[LAST_SIGNAL] = {0};
66
67 GType
68 modest_account_mgr_get_type (void)
69 {
70         static GType my_type = 0;
71
72         if (!my_type) {
73                 static const GTypeInfo my_info = {
74                         sizeof (ModestAccountMgrClass),
75                         modest_account_mgr_base_init,   /* base init */
76                         NULL,   /* base finalize */
77                         (GClassInitFunc) modest_account_mgr_class_init,
78                         NULL,   /* class finalize */
79                         NULL,   /* class data */
80                         sizeof (ModestAccountMgr),
81                         1,      /* n_preallocs */
82                         (GInstanceInitFunc) modest_account_mgr_init,
83                         NULL
84                 };
85
86                 my_type = g_type_register_static (G_TYPE_OBJECT,
87                                                   "ModestAccountMgr",
88                                                   &my_info, 0);
89         }
90         return my_type;
91 }
92
93 static void 
94 modest_account_mgr_base_init (gpointer g_class)
95 {
96         static gboolean modest_account_mgr_initialized = FALSE;
97
98         if (!modest_account_mgr_initialized) {
99                 /* signal definitions */
100                 signals[ACCOUNT_INSERTED_SIGNAL] =
101                         g_signal_new ("account_inserted",
102                                       MODEST_TYPE_ACCOUNT_MGR,
103                                       G_SIGNAL_RUN_FIRST,
104                                       G_STRUCT_OFFSET(ModestAccountMgrClass, account_inserted),
105                                       NULL, NULL,
106                                       g_cclosure_marshal_VOID__STRING,
107                                       G_TYPE_NONE, 1, G_TYPE_STRING);
108
109                 signals[ACCOUNT_REMOVED_SIGNAL] =
110                         g_signal_new ("account_removed",
111                                       MODEST_TYPE_ACCOUNT_MGR,
112                                       G_SIGNAL_RUN_FIRST,
113                                       G_STRUCT_OFFSET(ModestAccountMgrClass, account_removed),
114                                       NULL, NULL,
115                                       g_cclosure_marshal_VOID__STRING,
116                                       G_TYPE_NONE, 1, G_TYPE_STRING);
117
118                 signals[ACCOUNT_CHANGED_SIGNAL] =
119                         g_signal_new ("account_changed",
120                                       MODEST_TYPE_ACCOUNT_MGR,
121                                       G_SIGNAL_RUN_FIRST,
122                                       G_STRUCT_OFFSET(ModestAccountMgrClass, account_changed),
123                                       NULL, NULL,
124                                       modest_marshal_VOID__STRING_INT,
125                                       G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
126
127                 signals[ACCOUNT_BUSY_SIGNAL] =
128                         g_signal_new ("account_busy_changed",
129                                       MODEST_TYPE_ACCOUNT_MGR,
130                                       G_SIGNAL_RUN_FIRST,
131                                       G_STRUCT_OFFSET(ModestAccountMgrClass, account_busy_changed),
132                                       NULL, NULL,
133                                       modest_marshal_VOID__STRING_BOOLEAN,
134                                       G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
135
136                 signals[DEFAULT_ACCOUNT_CHANGED_SIGNAL] =
137                         g_signal_new ("default_account_changed",
138                                       MODEST_TYPE_ACCOUNT_MGR,
139                                       G_SIGNAL_RUN_FIRST,
140                                       G_STRUCT_OFFSET(ModestAccountMgrClass, default_account_changed),
141                                       NULL, NULL,
142                                       g_cclosure_marshal_VOID__VOID,
143                                       G_TYPE_NONE, 0);
144
145                 signals[DISPLAY_NAME_CHANGED_SIGNAL] =
146                         g_signal_new ("display_name_changed",
147                                       MODEST_TYPE_ACCOUNT_MGR,
148                                       G_SIGNAL_RUN_FIRST,
149                                       G_STRUCT_OFFSET(ModestAccountMgrClass, display_name_changed),
150                                       NULL, NULL,
151                                       g_cclosure_marshal_VOID__STRING,
152                                       G_TYPE_NONE, 1, G_TYPE_STRING);
153                 
154                 signals[ACCOUNT_UPDATED_SIGNAL] =
155                         g_signal_new ("account_updated",
156                                       MODEST_TYPE_ACCOUNT_MGR,
157                                       G_SIGNAL_RUN_FIRST,
158                                       G_STRUCT_OFFSET(ModestAccountMgrClass, account_updated),
159                                       NULL, NULL,
160                                       g_cclosure_marshal_VOID__STRING,
161                                       G_TYPE_NONE, 1, G_TYPE_STRING);
162
163
164                 modest_account_mgr_initialized = TRUE;
165         }
166 }
167
168 static void
169 modest_account_mgr_class_init (ModestAccountMgrClass * klass)
170 {
171         GObjectClass *gobject_class;
172         gobject_class = (GObjectClass *) klass;
173
174         parent_class = g_type_class_peek_parent (klass);
175         gobject_class->finalize = modest_account_mgr_finalize;
176
177         g_type_class_add_private (gobject_class,
178                                   sizeof (ModestAccountMgrPrivate));
179 }
180
181
182 static void
183 modest_account_mgr_init (ModestAccountMgr * obj)
184 {
185         ModestAccountMgrPrivate *priv =
186                 MODEST_ACCOUNT_MGR_GET_PRIVATE (obj);
187
188         priv->modest_conf   = NULL;
189         priv->busy_accounts = NULL;
190         priv->timeout       = 0;
191         
192         priv->notification_id_accounts = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free);
193
194         /* we maintain hashes for the modest-conf keys we build from account name
195          * + key. many seem to be used often, and generating them showed up high
196          * in oprofile */
197         /* both hashes are hashes to hashes;
198          * account-key => keyname ==> account-key-name
199          */     
200         priv->server_account_key_hash = g_hash_table_new_full (g_str_hash,
201                                                                g_str_equal,
202                                                                g_free,
203                                                                (GDestroyNotify)g_hash_table_destroy);
204         priv->account_key_hash        = g_hash_table_new_full (g_str_hash,
205                                                                g_str_equal,
206                                                                g_free,
207                                                                (GDestroyNotify)g_hash_table_destroy);
208
209         /* FALSE means: status is unknown */
210         priv->has_accounts = FALSE;
211         priv->has_enabled_accounts = FALSE;
212 }
213
214 static void
215 modest_account_mgr_finalize (GObject * obj)
216 {
217         ModestAccountMgrPrivate *priv = 
218                 MODEST_ACCOUNT_MGR_GET_PRIVATE (obj);
219
220         if (priv->notification_id_accounts) {
221                 /* TODO: forget dirs */
222
223                 g_hash_table_destroy (priv->notification_id_accounts);
224         }
225
226         if (priv->modest_conf) {
227                 g_object_unref (G_OBJECT(priv->modest_conf));
228                 priv->modest_conf = NULL;
229         }
230
231         if (priv->timeout)
232                 g_source_remove (priv->timeout);
233         priv->timeout = 0;
234
235         if (priv->server_account_key_hash) {
236                 g_hash_table_destroy (priv->server_account_key_hash);
237                 priv->server_account_key_hash = NULL;
238         }
239         
240         if (priv->account_key_hash) {
241                 g_hash_table_destroy (priv->account_key_hash);
242                 priv->account_key_hash = NULL;
243         }
244
245         G_OBJECT_CLASS(parent_class)->finalize (obj);
246 }
247
248
249 ModestAccountMgr *
250 modest_account_mgr_new (ModestConf *conf)
251 {
252         GObject *obj;
253         ModestAccountMgrPrivate *priv;
254
255         g_return_val_if_fail (conf, NULL);
256
257         obj = G_OBJECT (g_object_new (MODEST_TYPE_ACCOUNT_MGR, NULL));
258         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (obj);
259
260         g_object_ref (G_OBJECT(conf));
261         priv->modest_conf = conf;
262
263         return MODEST_ACCOUNT_MGR (obj);
264 }
265
266
267 static const gchar *
268 null_means_empty (const gchar * str)
269 {
270         return str ? str : "";
271 }
272
273 gboolean
274 modest_account_mgr_add_account_from_settings (ModestAccountMgr *self,
275                                               ModestAccountSettings *settings)
276 {
277         ModestAccountMgrPrivate *priv;
278         const gchar* display_name;
279         gchar *account_name_start, *account_name;
280         gchar *store_name_start, *store_name;
281         gchar *transport_name_start, *transport_name;
282         gchar *default_account;
283         ModestServerAccountSettings *store_settings, *transport_settings;
284
285         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR (self), FALSE);
286         g_return_val_if_fail (MODEST_IS_ACCOUNT_SETTINGS (settings), FALSE);
287
288         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
289         display_name = modest_account_settings_get_display_name (settings);
290
291         /* We should have checked for this already, and changed that name accordingly, 
292          * but let's check again just in case */
293         if (!display_name || 
294             modest_account_mgr_account_with_display_name_exists (self, display_name)) {
295                 return FALSE;
296         }
297
298         /* Increment the non-user visible name if necessary, 
299          * based on the display name: */
300         account_name_start = g_strdup_printf ("%sID", display_name);
301         account_name = modest_account_mgr_get_unused_account_name (self,
302                                                                    account_name_start, FALSE /* not a server account */);
303         g_free (account_name_start);
304         
305         /* Add a (incoming) server account, to be used by the account: */
306         store_name_start = g_strconcat (account_name, "_store", NULL);
307         store_name = modest_account_mgr_get_unused_account_name (self, 
308                                                                  store_name_start, TRUE /* server account */);
309         g_free (store_name_start);
310         
311         /* Add a (outgoing) server account to be used by the account: */
312         transport_name_start = g_strconcat (account_name, "_transport", NULL);
313         transport_name = modest_account_mgr_get_unused_account_name (self, 
314                                                                      transport_name_start, TRUE /* server account */);
315         g_free (transport_name_start);
316
317         modest_account_settings_set_account_name (settings, account_name);
318         store_settings = modest_account_settings_get_store_settings (settings);
319         modest_server_account_settings_set_account_name (store_settings, store_name);
320         transport_settings = modest_account_settings_get_transport_settings (settings);
321         modest_server_account_settings_set_account_name (transport_settings, transport_name);
322         g_object_unref (store_settings);
323         g_object_unref (transport_settings);
324
325         /* Create the account, which will contain the two "server accounts": */
326         modest_account_mgr_save_account_settings (self, settings);
327         g_free (store_name);
328         g_free (transport_name);
329         
330         /* Sanity check: */
331         /* There must be at least one account now: */
332         /* Note, when this fails is is caused by a Maemo gconf bug that has been 
333          * fixed in versions after 3.1. */
334         if(!modest_account_mgr_has_accounts (self, FALSE))
335                 g_warning ("modest_account_mgr_account_names() returned NULL after adding an account.");
336                                 
337         /* Notify the observers */
338         g_signal_emit (self, signals[ACCOUNT_INSERTED_SIGNAL], 0, account_name);
339
340         /* if no default account has been defined yet, do so now */
341         default_account = modest_account_mgr_get_default_account (self);
342         if (!default_account) {
343                 modest_account_mgr_set_default_account (self, account_name);
344                 modest_account_settings_set_is_default (settings, TRUE);
345         }
346         g_free (default_account);
347         g_free (account_name);
348
349         /* (re)set the automatic account update */
350         modest_platform_set_update_interval
351                 (modest_conf_get_int (priv->modest_conf, MODEST_CONF_UPDATE_INTERVAL, NULL));
352
353         return TRUE;
354 }
355
356
357 gboolean
358 modest_account_mgr_add_account (ModestAccountMgr *self,
359                                 const gchar *name,
360                                 const gchar *display_name,
361                                 const gchar *user_fullname,
362                                 const gchar *user_email,
363                                 ModestAccountRetrieveType retrieve_type,
364                                 const gchar *store_account,
365                                 const gchar *transport_account,
366                                 gboolean enabled)
367 {
368         ModestAccountMgrPrivate *priv;
369         const gchar *key;
370         gboolean ok;
371         gchar *default_account;
372         GError *err = NULL;
373         
374         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
375         g_return_val_if_fail (name, FALSE);
376         g_return_val_if_fail (strchr(name, '/') == NULL, FALSE);
377         
378         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
379
380         /*
381          * we create the account by adding an account 'dir', with the name <name>,
382          * and in that the 'display_name' string key
383          */
384         key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_DISPLAY_NAME,
385                                                               FALSE);
386         if (modest_account_mgr_account_exists (self, key, FALSE)) {
387                 g_printerr ("modest: account already exists\n");
388                 return FALSE;
389         }
390         
391         ok = modest_conf_set_string (priv->modest_conf, key, name, &err);
392         if (!ok) {
393                 g_printerr ("modest: cannot set display name\n");
394                 if (err) {
395                         g_printerr ("modest: Error adding account conf: %s\n", err->message);
396                         g_error_free (err);
397                 }
398                 return FALSE;
399         }
400         
401         if (store_account) {
402                 key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_STORE_ACCOUNT,
403                                                                       FALSE);
404                 ok = modest_conf_set_string (priv->modest_conf, key, store_account, &err);
405                 if (!ok) {
406                         g_printerr ("modest: failed to set store account '%s'\n",
407                                     store_account);
408                         if (err) {
409                                 g_printerr ("modest: Error adding store account conf: %s\n", err->message);
410                                 g_error_free (err);
411                         }
412                         return FALSE;
413                 }
414         }
415         
416         if (transport_account) {
417                 key = _modest_account_mgr_get_account_keyname_cached (priv, name,
418                                                                       MODEST_ACCOUNT_TRANSPORT_ACCOUNT,
419                                                                       FALSE);
420                 ok = modest_conf_set_string (priv->modest_conf, key, transport_account, &err);
421                 if (!ok) {
422                         g_printerr ("modest: failed to set transport account '%s'\n",
423                                     transport_account);
424                         if (err) {
425                                 g_printerr ("modest: Error adding transport account conf: %s\n", err->message);
426                                 g_error_free (err);
427                         }       
428                         return FALSE;
429                 }
430         }
431
432         /* Make sure that leave-messages-on-server is enabled by default, 
433          * as per the UI spec, though it is only meaningful for accounts using POP.
434          * (possibly this gconf key should be under the server account): */
435         modest_account_mgr_set_bool (self, name, MODEST_ACCOUNT_LEAVE_ON_SERVER, TRUE, FALSE);
436         modest_account_mgr_set_bool (self, name, MODEST_ACCOUNT_ENABLED, enabled,FALSE);
437
438         /* Fill other data */
439         modest_account_mgr_set_string (self, name,
440                                        MODEST_ACCOUNT_DISPLAY_NAME, 
441                                        display_name, FALSE);
442         modest_account_mgr_set_string (self, name,
443                                        MODEST_ACCOUNT_FULLNAME, 
444                                        user_fullname, FALSE);
445         modest_account_mgr_set_string (self, name,
446                                        MODEST_ACCOUNT_EMAIL, 
447                                        user_email, FALSE);
448         modest_account_mgr_set_retrieve_type (self, name,
449                                               retrieve_type);
450
451         /* Notify the observers */
452         g_signal_emit (self, signals[ACCOUNT_INSERTED_SIGNAL], 0, name);
453
454         /* if no default account has been defined yet, do so now */
455         default_account = modest_account_mgr_get_default_account (self);
456         if (!default_account)
457                 modest_account_mgr_set_default_account (self, name);
458         g_free (default_account);
459         
460         /* (re)set the automatic account update */
461         modest_platform_set_update_interval
462                 (modest_conf_get_int (priv->modest_conf, MODEST_CONF_UPDATE_INTERVAL, NULL));
463         
464         return TRUE;
465 }
466
467
468 gboolean
469 modest_account_mgr_add_server_account (ModestAccountMgr * self,
470                                        const gchar *name, 
471                                        const gchar *hostname,
472                                        guint portnumber,
473                                        const gchar *username, 
474                                        const gchar *password,
475                                        ModestTransportStoreProtocol proto,
476                                        ModestConnectionProtocol security,
477                                        ModestAuthProtocol auth)
478 {
479         ModestAccountMgrPrivate *priv;
480         const gchar *key;
481         gboolean ok = TRUE;
482         GError *err = NULL;
483
484         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
485         g_return_val_if_fail (name, FALSE);
486         g_return_val_if_fail (strchr(name, '/') == NULL, FALSE);
487                               
488         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
489
490         /* hostname */
491         key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_HOSTNAME, TRUE);
492         if (modest_conf_key_exists (priv->modest_conf, key, &err)) {
493                 g_printerr ("modest: server account '%s' already exists\n", name);
494                 ok =  FALSE;
495         }
496         if (!ok)
497                 goto cleanup;
498         
499         modest_conf_set_string (priv->modest_conf, key, null_means_empty(hostname), &err);
500         if (err) {
501                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
502                 g_error_free (err);
503                 ok = FALSE;
504         }
505         if (!ok)
506                 goto cleanup;
507         
508         /* username */
509         key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_USERNAME, TRUE);
510         ok = modest_conf_set_string (priv->modest_conf, key, null_means_empty (username), &err);
511         if (err) {
512                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
513                 g_error_free (err);
514                 ok = FALSE;
515         }
516         if (!ok)
517                 goto cleanup;
518         
519         
520         /* password */
521         key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_PASSWORD, TRUE);
522         ok = modest_conf_set_string (priv->modest_conf, key, null_means_empty (password), &err);
523         if (err) {
524                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
525                 g_error_free (err);
526                 ok = FALSE;
527         }
528         if (!ok)
529                 goto cleanup;
530
531         /* proto */
532         key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_PROTO, TRUE);
533         ok = modest_conf_set_string (priv->modest_conf, key,
534                                      modest_protocol_info_get_transport_store_protocol_name(proto),
535                                      &err);
536         if (err) {
537                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
538                 g_error_free (err);
539                 ok = FALSE;
540         }
541         if (!ok)
542                 goto cleanup;
543
544
545         /* portnumber */
546         key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_PORT, TRUE);
547         ok = modest_conf_set_int (priv->modest_conf, key, portnumber, &err);
548         if (err) {
549                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
550                 g_error_free (err);
551                 ok = FALSE;
552         }
553         if (!ok)
554                 goto cleanup;
555
556         
557         /* auth mechanism */
558         key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_AUTH_MECH, TRUE);
559         ok = modest_conf_set_string (priv->modest_conf, key,
560                                      modest_protocol_info_get_auth_protocol_name (auth),
561                                      &err);
562         if (err) {
563                 g_printerr ("modest: failed to set %s: %s\n", key, err->message);
564                 g_error_free (err);
565                 ok = FALSE;
566         }
567         if (!ok)
568                 goto cleanup;
569         
570         /* Add the security settings: */
571         modest_account_mgr_set_server_account_security (self, name, security);
572
573 cleanup:
574         if (!ok) {
575                 g_printerr ("modest: failed to add server account\n");
576                 return FALSE;
577         }
578
579         return TRUE;
580 }
581
582 /** modest_account_mgr_add_server_account_uri:
583  * Only used for mbox and maildir accounts.
584  */
585 gboolean
586 modest_account_mgr_add_server_account_uri (ModestAccountMgr * self,
587                                            const gchar *name, 
588                                            ModestTransportStoreProtocol proto,
589                                            const gchar *uri)
590 {
591         ModestAccountMgrPrivate *priv;
592         const gchar *key;
593         gboolean ok;
594         
595         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
596         g_return_val_if_fail (name, FALSE);
597         g_return_val_if_fail (strchr(name, '/') == NULL, FALSE);
598         g_return_val_if_fail (uri, FALSE);
599         
600         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
601         
602         /* proto */
603         key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_PROTO, TRUE);
604         ok = modest_conf_set_string (priv->modest_conf, key,
605                                      modest_protocol_info_get_transport_store_protocol_name(proto),
606                                      NULL);
607
608         if (!ok) {
609                 g_printerr ("modest: failed to set proto\n");
610                 return FALSE;
611         }
612         
613         /* uri */
614         key = _modest_account_mgr_get_account_keyname_cached (priv, name, MODEST_ACCOUNT_URI, TRUE);
615         ok = modest_conf_set_string (priv->modest_conf, key, uri, NULL);
616
617         if (!ok) {
618                 g_printerr ("modest: failed to set uri\n");
619                 return FALSE;
620         }
621         return TRUE;
622 }
623
624 /* 
625  * Utility function used by modest_account_mgr_remove_account
626  */
627 static void
628 real_remove_account (ModestConf *conf,
629                      const gchar *acc_name,
630                      gboolean server_account)
631 {
632         GError *err = NULL;
633         gchar *key;
634         
635         key = _modest_account_mgr_get_account_keyname (acc_name, NULL, server_account);
636         modest_conf_remove_key (conf, key, &err);
637
638         if (err) {
639                 g_printerr ("modest: error removing key: %s\n", err->message);
640                 g_error_free (err);
641         }
642         g_free (key);
643 }
644
645 gboolean
646 modest_account_mgr_remove_account (ModestAccountMgr * self,
647                                    const gchar* name)
648 {
649         ModestAccountMgrPrivate *priv;
650         gchar *default_account_name, *store_acc_name, *transport_acc_name;
651         gboolean default_account_deleted;
652
653         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
654         g_return_val_if_fail (name, FALSE);
655
656         if (!modest_account_mgr_account_exists (self, name, FALSE)) {
657                 g_printerr ("modest: %s: account '%s' does not exist\n", __FUNCTION__, name);
658                 return FALSE;
659         }
660
661         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
662         default_account_deleted = FALSE;
663
664         /* If this was the default, then remove that setting: */
665         default_account_name = modest_account_mgr_get_default_account (self);
666         if (default_account_name && (strcmp (default_account_name, name) == 0)) {
667                 modest_account_mgr_unset_default_account (self);
668                 default_account_deleted = TRUE;
669         }
670         g_free (default_account_name);
671
672         /* Delete transport and store accounts */
673         store_acc_name = modest_account_mgr_get_string (self, name, 
674                                                         MODEST_ACCOUNT_STORE_ACCOUNT, FALSE);
675         if (store_acc_name)
676                 real_remove_account (priv->modest_conf, store_acc_name, TRUE);
677
678         transport_acc_name = modest_account_mgr_get_string (self, name, 
679                                                             MODEST_ACCOUNT_TRANSPORT_ACCOUNT, FALSE);
680         if (transport_acc_name)
681                 real_remove_account (priv->modest_conf, transport_acc_name, TRUE);
682                         
683         /* Remove the modest account */
684         real_remove_account (priv->modest_conf, name, FALSE);
685
686         if (default_account_deleted) {  
687                 /* pick another one as the new default account. We do
688                    this *after* deleting the keys, because otherwise a
689                    call to account_names will retrieve also the
690                    deleted account */
691                 modest_account_mgr_set_first_account_as_default (self);
692         }
693
694         /* if this was the last account, stop any auto-updating */
695         /* (re)set the automatic account update */
696         GSList *acc_names = modest_account_mgr_account_names (self, TRUE);
697         if (!acc_names) {
698                 modest_platform_set_update_interval (0);
699                 /* it was the last account, the has_account / has_enabled_account
700                  * changes
701                  */
702                 priv->has_accounts = priv->has_enabled_accounts = FALSE; 
703         } else
704                 modest_account_mgr_free_account_names (acc_names);
705         
706         /* Notify the observers. We do this *after* deleting
707            the keys, because otherwise a call to account_names
708            will retrieve also the deleted account */
709         g_signal_emit (G_OBJECT(self), signals[ACCOUNT_REMOVED_SIGNAL], 0, name);
710         
711         return TRUE;
712 }
713
714
715
716 /* strip the first /n/ character from each element
717  * caller must make sure all elements are strings with
718  * length >= n, and also that data can be freed.
719  * change is in-place
720  */
721 static void
722 strip_prefix_from_elements (GSList * lst, guint n)
723 {
724         while (lst) {
725                 memmove (lst->data, lst->data + n,
726                          strlen(lst->data) - n + 1);
727                 lst = lst->next;
728         }
729 }
730
731
732 GSList*
733 modest_account_mgr_account_names (ModestAccountMgr * self, gboolean only_enabled)
734 {
735         GSList *accounts;
736         ModestAccountMgrPrivate *priv;
737         GError *err = NULL;
738         
739         const size_t prefix_len = strlen (MODEST_ACCOUNT_NAMESPACE "/");
740
741         g_return_val_if_fail (self, NULL);
742
743         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
744         accounts = modest_conf_list_subkeys (priv->modest_conf,
745                                              MODEST_ACCOUNT_NAMESPACE, &err);
746
747         if (err) {
748                 g_printerr ("modest: failed to get subkeys (%s): %s\n",
749                             MODEST_ACCOUNT_NAMESPACE, err->message);
750                 g_error_free (err);
751                 return NULL; /* assume accounts did not get value when err is set...*/
752         }
753         
754         strip_prefix_from_elements (accounts, prefix_len);
755                 
756         GSList *result = NULL;
757         
758         /* Unescape the keys to get the account names: */
759         GSList *iter = accounts;
760         while (iter) {
761                 if (!(iter->data))
762                         continue;
763                         
764                 const gchar* account_name_key = (const gchar*)iter->data;
765                 gchar* unescaped_name = account_name_key ? 
766                         modest_conf_key_unescape (account_name_key) 
767                         : NULL;
768                 
769                 gboolean add = TRUE;
770                 if (only_enabled) {
771                         if (unescaped_name && 
772                             !modest_account_mgr_get_bool (self, unescaped_name, 
773                                                           MODEST_ACCOUNT_ENABLED, FALSE))
774                                 add = FALSE;
775                 }
776                 
777                 /* Ignore modest accounts whose server accounts don't exist: 
778                  * (We could be getting this list while the account is being deleted, 
779                  * while the child server accounts have already been deleted, but the 
780                  * parent modest account already exists.
781                  */
782                 if (add) {
783                         gchar* server_account_name = modest_account_mgr_get_string
784                                 (self, account_name_key, MODEST_ACCOUNT_STORE_ACCOUNT,
785                                  FALSE);
786                         if (server_account_name) {
787                                 if (!modest_account_mgr_account_exists (self, server_account_name, TRUE))
788                                         add = FALSE;
789                                 g_free (server_account_name);
790                         }
791                 }
792                 
793                 if (add) {
794                         gchar* server_account_name = modest_account_mgr_get_string
795                                 (self, account_name_key, MODEST_ACCOUNT_TRANSPORT_ACCOUNT,
796                                  FALSE);
797                         if (server_account_name) {
798                                 if (!modest_account_mgr_account_exists (self, server_account_name, TRUE))
799                                         add = FALSE;
800                                 g_free (server_account_name);
801                         }
802                 }
803                 
804                 if (add)
805                         result = g_slist_append (result, unescaped_name);
806                 else 
807                         g_free (unescaped_name);
808
809                 g_free (iter->data);
810                 iter->data = NULL;
811                 
812                 iter = g_slist_next (iter);     
813         }
814         
815
816         /* we already freed the strings in the loop */
817         g_slist_free (accounts);
818         
819         accounts = NULL;
820         return result;
821 }
822
823
824 void
825 modest_account_mgr_free_account_names (GSList *account_names)
826 {
827         g_slist_foreach (account_names, (GFunc)g_free, NULL);
828         g_slist_free (account_names);
829 }
830
831
832
833 gchar *
834 modest_account_mgr_get_string (ModestAccountMgr *self, const gchar *name,
835                                const gchar *key, gboolean server_account) {
836
837         ModestAccountMgrPrivate *priv;
838
839         const gchar *keyname;
840         gchar *retval;
841         GError *err = NULL;
842
843         g_return_val_if_fail (self, NULL);
844         g_return_val_if_fail (name, NULL);
845         g_return_val_if_fail (key, NULL);
846
847         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
848         
849         keyname = _modest_account_mgr_get_account_keyname_cached (priv, name, key, server_account);
850         
851         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
852         retval = modest_conf_get_string (priv->modest_conf, keyname, &err);     
853         if (err) {
854                 g_printerr ("modest: error getting string '%s': %s\n", keyname, err->message);
855                 g_error_free (err);
856                 retval = NULL;
857         }
858
859         return retval;
860 }
861
862
863 gint
864 modest_account_mgr_get_int (ModestAccountMgr *self, const gchar *name, const gchar *key,
865                             gboolean server_account)
866 {
867         ModestAccountMgrPrivate *priv;
868
869         const gchar *keyname;
870         gint retval;
871         GError *err = NULL;
872         
873         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), -1);
874         g_return_val_if_fail (name, -1);
875         g_return_val_if_fail (key, -1);
876
877         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
878
879         keyname = _modest_account_mgr_get_account_keyname_cached (priv, name, key, server_account);
880         
881         retval = modest_conf_get_int (priv->modest_conf, keyname, &err);
882         if (err) {
883                 g_printerr ("modest: error getting int '%s': %s\n", keyname, err->message);
884                 g_error_free (err);
885                 retval = -1;
886         }
887
888         return retval;
889 }
890
891
892
893 gboolean
894 modest_account_mgr_get_bool (ModestAccountMgr * self, const gchar *account,
895                              const gchar * key, gboolean server_account)
896 {
897         ModestAccountMgrPrivate *priv;
898
899         const gchar *keyname;
900         gboolean retval;
901         GError *err = NULL;
902
903         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
904         g_return_val_if_fail (account, FALSE);
905         g_return_val_if_fail (key, FALSE);
906
907         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
908         ///keyname = _modest_account_mgr_get_account_keyname (account, key, server_account);
909
910         keyname = _modest_account_mgr_get_account_keyname_cached (priv, account, key, server_account);
911                 
912         retval = modest_conf_get_bool (priv->modest_conf, keyname, &err);               
913         if (err) {
914                 g_printerr ("modest: error getting bool '%s': %s\n", keyname, err->message);
915                 g_error_free (err);
916                 retval = FALSE;
917         }
918
919         return retval;
920 }
921
922
923
924 GSList * 
925 modest_account_mgr_get_list (ModestAccountMgr *self, const gchar *name,
926                              const gchar *key, ModestConfValueType list_type,
927                              gboolean server_account)
928 {
929         ModestAccountMgrPrivate *priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
930
931         const gchar *keyname;
932         GSList *retval;
933         GError *err = NULL;
934         
935         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), NULL);
936         g_return_val_if_fail (name, NULL);
937         g_return_val_if_fail (key, NULL);
938         
939         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
940
941         keyname = _modest_account_mgr_get_account_keyname_cached (priv, name, key,
942                                                                   server_account);
943         
944         retval = modest_conf_get_list (priv->modest_conf, keyname, list_type, &err);
945         if (err) {
946                 g_printerr ("modest: error getting list '%s': %s\n", keyname,
947                             err->message);
948                 g_error_free (err);
949                 retval = FALSE;
950         }
951         return retval;
952 }
953
954
955 gboolean
956 modest_account_mgr_set_string (ModestAccountMgr * self, 
957                                const gchar * name,
958                                const gchar * key, 
959                                const gchar * val, 
960                                gboolean server_account)
961 {
962         ModestAccountMgrPrivate *priv;
963
964         const gchar *keyname;
965         gboolean retval;
966         GError *err = NULL;
967
968         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
969         g_return_val_if_fail (name, FALSE);
970         g_return_val_if_fail (key, FALSE);
971
972         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
973
974         keyname = _modest_account_mgr_get_account_keyname_cached (priv, name, key, server_account);
975         
976         retval = modest_conf_set_string (priv->modest_conf, keyname, val, &err);
977         if (err) {
978                 g_printerr ("modest: error setting string '%s': %s\n", keyname, err->message);
979                 g_error_free (err);
980                 retval = FALSE;
981         }
982         return retval;
983 }
984
985 gboolean
986 modest_account_mgr_set_int (ModestAccountMgr * self, const gchar * name,
987                             const gchar * key, int val, gboolean server_account)
988 {
989         ModestAccountMgrPrivate *priv;
990         const gchar *keyname;
991         gboolean retval;
992         GError *err = NULL;
993         
994         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
995         g_return_val_if_fail (name, FALSE);
996         g_return_val_if_fail (key, FALSE);
997
998         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
999
1000         keyname = _modest_account_mgr_get_account_keyname_cached (priv, name, key, server_account);
1001         
1002         retval = modest_conf_set_int (priv->modest_conf, keyname, val, &err);
1003         if (err) {
1004                 g_printerr ("modest: error setting int '%s': %s\n", keyname, err->message);
1005                 g_error_free (err);
1006                 retval = FALSE;
1007         } else {
1008                 /* check whether this field is one of those interesting for the 
1009                  * "account-updated" signal */
1010                 if (strcmp(key, MODEST_ACCOUNT_LAST_UPDATED) == 0) {
1011                         g_signal_emit (G_OBJECT(self), signals[ACCOUNT_UPDATED_SIGNAL], 
1012                                         0, name);
1013                 }
1014         }
1015         return retval;
1016 }
1017
1018
1019
1020 gboolean
1021 modest_account_mgr_set_bool (ModestAccountMgr * self, const gchar * name,
1022                              const gchar * key, gboolean val, gboolean server_account)
1023 {
1024         ModestAccountMgrPrivate *priv;
1025
1026         const gchar *keyname;
1027         gboolean retval;
1028         GError *err = NULL;
1029
1030         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
1031         g_return_val_if_fail (name, FALSE);
1032         g_return_val_if_fail (key, FALSE);
1033
1034         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1035         keyname = _modest_account_mgr_get_account_keyname_cached (priv, name, key, server_account);
1036         
1037         retval = modest_conf_set_bool (priv->modest_conf, keyname, val, &err);
1038         if (err) {
1039                 g_printerr ("modest: error setting bool '%s': %s\n", keyname, err->message);
1040                 g_error_free (err);
1041                 retval = FALSE;
1042         }
1043
1044         return retval;
1045 }
1046
1047
1048 gboolean
1049 modest_account_mgr_set_list (ModestAccountMgr *self,
1050                              const gchar *name,
1051                              const gchar *key,
1052                              GSList *val,
1053                              ModestConfValueType list_type,
1054                              gboolean server_account)
1055 {
1056         ModestAccountMgrPrivate *priv;
1057         const gchar *keyname;
1058         GError *err = NULL;
1059         gboolean retval;
1060         
1061         g_return_val_if_fail (self, FALSE);
1062         g_return_val_if_fail (name, FALSE);
1063         g_return_val_if_fail (key,  FALSE);
1064         g_return_val_if_fail (val,  FALSE);
1065
1066         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1067
1068         keyname = _modest_account_mgr_get_account_keyname_cached (priv, name, key, server_account);
1069         
1070         retval = modest_conf_set_list (priv->modest_conf, keyname, val, list_type, &err);
1071         if (err) {
1072                 g_printerr ("modest: error setting list '%s': %s\n", keyname, err->message);
1073                 g_error_free (err);
1074                 retval = FALSE;
1075         }
1076
1077         return retval;
1078 }
1079
1080 gboolean
1081 modest_account_mgr_account_exists (ModestAccountMgr * self, const gchar* name,
1082                                    gboolean server_account)
1083 {
1084         ModestAccountMgrPrivate *priv;
1085
1086         const gchar *keyname;
1087         gboolean retval;
1088         GError *err = NULL;
1089
1090         g_return_val_if_fail (self, FALSE);
1091         g_return_val_if_fail (name, FALSE);
1092
1093         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1094         keyname = _modest_account_mgr_get_account_keyname_cached (priv, name, NULL, server_account);
1095         retval = modest_conf_key_exists (priv->modest_conf, keyname, &err);
1096         if (err) {
1097                 g_printerr ("modest: error determining existance of '%s': %s\n", keyname,
1098                             err->message);
1099                 g_error_free (err);
1100                 retval = FALSE;
1101         }
1102         return retval;
1103 }
1104
1105 gboolean
1106 modest_account_mgr_account_with_display_name_exists  (ModestAccountMgr *self, 
1107                                                       const gchar *display_name)
1108 {
1109         GSList *account_names = NULL;
1110         GSList *cursor = NULL;
1111         
1112         cursor = account_names = modest_account_mgr_account_names (self, 
1113                                                                    TRUE /* enabled accounts, because disabled accounts are not user visible. */);
1114
1115         gboolean found = FALSE;
1116         
1117         /* Look at each non-server account to check their display names; */
1118         while (cursor) {
1119                 const gchar *account_name = (gchar*)cursor->data;
1120                 const gchar *cursor_display_name;
1121                 
1122                 ModestAccountSettings *settings = modest_account_mgr_load_account_settings (self, account_name);
1123                 if (!settings) {
1124                         g_printerr ("modest: failed to get account data for %s\n", account_name);
1125                         continue;
1126                 }
1127
1128                 cursor_display_name = modest_account_settings_get_display_name (settings);
1129                 if(cursor_display_name && (strcmp (cursor_display_name, display_name) == 0)) {
1130                         found = TRUE;
1131                         g_object_unref (settings);
1132                         break;
1133                 }
1134
1135                 g_object_unref (settings);
1136                 cursor = cursor->next;
1137         }
1138         modest_account_mgr_free_account_names (account_names);
1139         account_names = NULL;
1140         
1141         return found;
1142 }
1143
1144
1145
1146
1147 gboolean 
1148 modest_account_mgr_unset (ModestAccountMgr *self, const gchar *name,
1149                           const gchar *key, gboolean server_account)
1150 {
1151         ModestAccountMgrPrivate *priv;
1152         
1153         const gchar *keyname;
1154         gboolean retval;
1155         GError *err = NULL;
1156         
1157         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
1158         g_return_val_if_fail (name, FALSE);
1159         g_return_val_if_fail (key, FALSE);
1160
1161         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1162         keyname = _modest_account_mgr_get_account_keyname_cached (priv, name, key, server_account);
1163
1164         retval = modest_conf_remove_key (priv->modest_conf, keyname, &err);
1165         if (err) {
1166                 g_printerr ("modest: error unsetting'%s': %s\n", keyname,
1167                             err->message);
1168                 g_error_free (err);
1169                 retval = FALSE;
1170         }
1171         return retval;
1172 }
1173
1174 gchar*
1175 _modest_account_mgr_account_from_key (const gchar *key, gboolean *is_account_key, gboolean *is_server_account)
1176 {
1177         /* Initialize input parameters: */
1178         if (is_account_key)
1179                 *is_account_key = FALSE;
1180
1181         if (is_server_account)
1182                 *is_server_account = FALSE;
1183
1184         const gchar* account_ns        = MODEST_ACCOUNT_NAMESPACE "/";
1185         const gchar* server_account_ns = MODEST_SERVER_ACCOUNT_NAMESPACE "/";
1186         gchar *cursor;
1187         gchar *account = NULL;
1188
1189         /* determine whether it's an account or a server account,
1190          * based on the prefix */
1191         if (g_str_has_prefix (key, account_ns)) {
1192
1193                 if (is_server_account)
1194                         *is_server_account = FALSE;
1195                 
1196                 account = g_strdup (key + strlen (account_ns));
1197
1198         } else if (g_str_has_prefix (key, server_account_ns)) {
1199
1200                 if (is_server_account)
1201                         *is_server_account = TRUE;
1202                 
1203                 account = g_strdup (key + strlen (server_account_ns));  
1204         } else
1205                 return NULL;
1206
1207         /* if there are any slashes left in the key, it's not
1208          * the toplevel entry for an account
1209          */
1210         cursor = strstr(account, "/");
1211         
1212         if (is_account_key && cursor)
1213                 *is_account_key = TRUE;
1214
1215         /* put a NULL where the first slash was */
1216         if (cursor)
1217                 *cursor = '\0';
1218
1219         if (account) {
1220                 /* The key is an escaped string, so unescape it to get the actual account name: */
1221                 gchar *unescaped_name = modest_conf_key_unescape (account);
1222                 g_free (account);
1223                 return unescaped_name;
1224         } else
1225                 return NULL;
1226 }
1227
1228
1229
1230
1231
1232 /* optimization: only with non-alphanum chars, escaping is needed */
1233 inline static gboolean
1234 is_alphanum (const gchar* str)
1235 {
1236         const gchar *cursor;
1237         for (cursor = str; cursor && *cursor; ++cursor) {
1238                 const char c = *cursor;
1239                 /* we cannot trust isalnum(3), because it might consider locales */
1240                 /*       numbers            ALPHA            alpha       */
1241                 if (!((c>=48 && c<=57)||(c>=65 && c<=90)||(c>=97 && c<=122)))
1242                         return FALSE;
1243         }
1244         return TRUE;
1245 }
1246                 
1247
1248
1249
1250 /* must be freed by caller */
1251 gchar *
1252 _modest_account_mgr_get_account_keyname (const gchar *account_name, const gchar* name,
1253                                          gboolean server_account)
1254 {
1255         gchar *retval = NULL;   
1256         gchar *namespace = server_account ? MODEST_SERVER_ACCOUNT_NAMESPACE : MODEST_ACCOUNT_NAMESPACE;
1257         gchar *escaped_account_name, *escaped_name;
1258         
1259         if (!account_name)
1260                 return g_strdup (namespace);
1261
1262         /* optimization: only escape names when need to be escaped */
1263         if (is_alphanum (account_name))
1264                 escaped_account_name = (gchar*)account_name;
1265         else
1266                 escaped_account_name = modest_conf_key_escape (account_name);
1267         
1268         if (is_alphanum (name))
1269                 escaped_name = (gchar*)name;
1270         else
1271                 escaped_name = modest_conf_key_escape (name);
1272         //////////////////////////////////////////////////////////////
1273
1274         if (escaped_account_name && escaped_name)
1275                 retval = g_strconcat (namespace, "/", escaped_account_name, "/", escaped_name, NULL);
1276         else if (escaped_account_name)
1277                 retval = g_strconcat (namespace, "/", escaped_account_name, NULL);
1278
1279         /* Sanity check: */
1280         if (!retval || !modest_conf_key_is_valid (retval)) {
1281                 g_warning ("%s: Generated conf key was invalid: %s", __FUNCTION__,
1282                            retval ? retval: "<empty>");
1283                 g_free (retval);
1284                 retval = NULL;
1285         }
1286         
1287         /* g_free is only needed if we actually allocated anything */
1288         if (name != escaped_name)
1289                 g_free (escaped_name);
1290         if (account_name != escaped_account_name)
1291                 g_free (escaped_account_name);
1292
1293         return retval;
1294 }
1295
1296 static const gchar *
1297 _modest_account_mgr_get_account_keyname_cached (ModestAccountMgrPrivate *priv, 
1298                                                 const gchar* account_name,
1299                                                 const gchar *name, 
1300                                                 gboolean is_server)
1301 {
1302         GHashTable *hash = is_server ? priv->server_account_key_hash : priv->account_key_hash;
1303         GHashTable *account_hash;
1304         gchar *key = NULL;
1305         const gchar *search_name;
1306
1307         if (!account_name)
1308                 return is_server ? MODEST_SERVER_ACCOUNT_NAMESPACE : MODEST_ACCOUNT_NAMESPACE;
1309
1310         search_name = name ? name : "<dummy>";
1311         
1312         account_hash = g_hash_table_lookup (hash, account_name);        
1313         if (!account_hash) { /* no hash for this account yet? create it */
1314                 account_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);         
1315                 key = _modest_account_mgr_get_account_keyname (account_name, name, is_server);
1316                 g_hash_table_insert (account_hash, g_strdup(search_name), key);
1317                 g_hash_table_insert (hash, g_strdup(account_name), account_hash);
1318                 return key;
1319         }
1320         
1321         /* we have a hash for this account, but do we have the key? */  
1322         key = g_hash_table_lookup (account_hash, search_name);
1323         if (!key) {
1324                 key = _modest_account_mgr_get_account_keyname (account_name, name, is_server);
1325                 g_hash_table_insert (account_hash, g_strdup(search_name), key);
1326         }
1327         
1328         return key;
1329 }
1330
1331
1332 gboolean
1333 modest_account_mgr_has_accounts (ModestAccountMgr* self, gboolean enabled)
1334 {
1335         ModestAccountMgrPrivate* priv;
1336         GSList *account_names;
1337         gboolean accounts_exist;
1338
1339         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
1340         
1341         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1342         
1343         if (enabled && priv->has_enabled_accounts)
1344                 return TRUE;
1345         else if (priv->has_accounts)
1346                 return TRUE;
1347                 
1348         /* Check that at least one account exists: */
1349         account_names = modest_account_mgr_account_names (self,enabled);
1350         accounts_exist = account_names != NULL;
1351         modest_account_mgr_free_account_names (account_names);
1352         account_names = NULL;
1353
1354         /* cache it. */
1355         if (enabled)
1356                 priv->has_enabled_accounts = accounts_exist;
1357         else
1358                 priv->has_accounts = accounts_exist;
1359         
1360         return accounts_exist;
1361 }
1362
1363 static int
1364 compare_account_name(gconstpointer a, gconstpointer b)
1365 {
1366         const gchar* account_name = (const gchar*) a;
1367         const gchar* account_name2 = (const gchar*) b;
1368         return strcmp(account_name, account_name2);
1369 }
1370
1371 void 
1372 modest_account_mgr_set_account_busy(ModestAccountMgr* self, 
1373                                     const gchar* account_name, 
1374                                     gboolean busy)
1375 {
1376         ModestAccountMgrPrivate* priv;
1377
1378         g_return_if_fail (MODEST_IS_ACCOUNT_MGR(self));
1379         g_return_if_fail (account_name);
1380
1381         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1382         if (busy) {
1383                 GSList *account_names = modest_account_mgr_account_names (self, TRUE);
1384                 GSList* account = g_slist_find_custom(account_names, account_name, 
1385                                                       (GCompareFunc) compare_account_name);
1386
1387                 if (account && !modest_account_mgr_account_is_busy(self, account_name)) {
1388                         priv->busy_accounts = g_slist_append(priv->busy_accounts, g_strdup(account_name));
1389                         g_signal_emit (G_OBJECT(self), signals[ACCOUNT_BUSY_SIGNAL], 
1390                                        0, account_name, TRUE);
1391                 }
1392                 modest_account_mgr_free_account_names (account_names);
1393                 account_names = NULL;
1394         } else {
1395                 GSList* account = 
1396                         g_slist_find_custom(priv->busy_accounts, account_name, (GCompareFunc) compare_account_name);
1397
1398                 if (account) {
1399                         g_free(account->data);
1400                         priv->busy_accounts = g_slist_delete_link(priv->busy_accounts, account);
1401                         g_signal_emit (G_OBJECT(self), signals[ACCOUNT_BUSY_SIGNAL], 
1402                                        0, account_name, FALSE);
1403                 }
1404         }
1405 }
1406
1407 gboolean
1408 modest_account_mgr_account_is_busy (ModestAccountMgr* self, const gchar* account_name)
1409 {
1410         ModestAccountMgrPrivate* priv;
1411         
1412         g_return_val_if_fail (MODEST_IS_ACCOUNT_MGR(self), FALSE);
1413         g_return_val_if_fail (account_name, FALSE);
1414
1415         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1416                 
1417         return (g_slist_find_custom(priv->busy_accounts, account_name, (GCompareFunc) compare_account_name)
1418                 != NULL);
1419 }
1420
1421 void
1422 modest_account_mgr_notify_account_update (ModestAccountMgr* self, 
1423                                           const gchar *server_account_name)
1424 {
1425         ModestTransportStoreProtocol proto;
1426         ModestAccountMgrPrivate* priv;
1427         gchar *proto_name = NULL;
1428
1429         g_return_if_fail (self);
1430         g_return_if_fail (server_account_name);
1431         
1432         priv = MODEST_ACCOUNT_MGR_GET_PRIVATE (self);
1433         
1434         /* Get protocol */
1435         proto_name = modest_account_mgr_get_string (self, server_account_name, 
1436                                                     MODEST_ACCOUNT_PROTO, TRUE);
1437         if (!proto_name) {
1438                 g_free (proto_name);
1439                 g_return_if_reached ();
1440         }
1441         proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1442         g_free (proto_name);
1443
1444         /* there is some update in the account, so we can't
1445          * be sure about whether there are still enabled accounts...
1446          */
1447         priv->has_enabled_accounts = FALSE;
1448         priv->has_accounts         = FALSE;
1449         
1450         /* Emit "update-account" */
1451         g_signal_emit (G_OBJECT(self), 
1452                        signals[ACCOUNT_CHANGED_SIGNAL], 0, 
1453                        server_account_name, 
1454                        (modest_protocol_info_protocol_is_store (proto)) ? 
1455                        TNY_ACCOUNT_TYPE_STORE : 
1456                        TNY_ACCOUNT_TYPE_TRANSPORT);
1457 }
1458
1459
1460 gboolean
1461 modest_account_mgr_set_default_account  (ModestAccountMgr *self, const gchar* account)
1462 {
1463         ModestConf *conf;
1464         gboolean retval;
1465         
1466         g_return_val_if_fail (self,    FALSE);
1467         g_return_val_if_fail (account, FALSE);
1468         g_return_val_if_fail (modest_account_mgr_account_exists (self, account, FALSE),
1469                               FALSE);
1470         
1471         conf = MODEST_ACCOUNT_MGR_GET_PRIVATE (self)->modest_conf;
1472
1473         /* Change the default account and notify */
1474         retval = modest_conf_set_string (conf, MODEST_CONF_DEFAULT_ACCOUNT, account, NULL);
1475         if (retval)
1476                 g_signal_emit (G_OBJECT(self), signals[DEFAULT_ACCOUNT_CHANGED_SIGNAL], 0);
1477
1478         return retval;
1479 }
1480
1481
1482 gchar*
1483 modest_account_mgr_get_default_account  (ModestAccountMgr *self)
1484 {
1485         gchar *account; 
1486         ModestConf *conf;
1487         GError *err = NULL;
1488         
1489         g_return_val_if_fail (self, NULL);
1490
1491         conf = MODEST_ACCOUNT_MGR_GET_PRIVATE (self)->modest_conf;
1492         account = modest_conf_get_string (conf, MODEST_CONF_DEFAULT_ACCOUNT, &err);
1493         
1494         if (err) {
1495                 g_printerr ("modest: failed to get '%s': %s\n",
1496                             MODEST_CONF_DEFAULT_ACCOUNT, err->message);
1497                 g_error_free (err);
1498                 g_free (account);
1499                 return  NULL;
1500         }
1501         
1502         /* sanity check */
1503         if (account && !modest_account_mgr_account_exists (self, account, FALSE)) {
1504                 g_printerr ("modest: default account does not exist\n");
1505                 g_free (account);
1506                 return NULL;
1507         }
1508
1509         return account;
1510 }
1511
1512 static gboolean
1513 modest_account_mgr_unset_default_account (ModestAccountMgr *self)
1514 {
1515         ModestConf *conf;
1516         gboolean retval;
1517         
1518         g_return_val_if_fail (self,    FALSE);
1519
1520         conf = MODEST_ACCOUNT_MGR_GET_PRIVATE (self)->modest_conf;
1521                 
1522         retval = modest_conf_remove_key (conf, MODEST_CONF_DEFAULT_ACCOUNT, NULL /* err */);
1523
1524         if (retval)
1525                 g_signal_emit (G_OBJECT(self), signals[DEFAULT_ACCOUNT_CHANGED_SIGNAL], 0);
1526
1527         return retval;
1528 }
1529
1530
1531 gchar* 
1532 modest_account_mgr_get_display_name (ModestAccountMgr *self, 
1533                                      const gchar* name)
1534 {
1535         return modest_account_mgr_get_string (self, name, MODEST_ACCOUNT_DISPLAY_NAME, FALSE);
1536 }
1537
1538 void 
1539 modest_account_mgr_set_display_name (ModestAccountMgr *self, 
1540                                      const gchar *account_name,
1541                                      const gchar *display_name)
1542 {
1543         gboolean notify = TRUE;
1544
1545         if (!modest_account_mgr_get_display_name (self, account_name))
1546                 notify = FALSE;
1547
1548         modest_account_mgr_set_string (self, 
1549                                        account_name,
1550                                        MODEST_ACCOUNT_DISPLAY_NAME, 
1551                                        display_name, 
1552                                        FALSE /* not server account */);
1553
1554         /* Notify about the change in the display name */
1555         if (notify)
1556                 g_signal_emit (self, signals[DISPLAY_NAME_CHANGED_SIGNAL], 0, account_name);
1557 }